Multiproceso en Java
La máquina virtual de Java admite la computación paralela . Todos los cálculos se pueden realizar en el contexto de uno o más subprocesos. Podemos configurar fácilmente el acceso al mismo recurso u objeto para múltiples subprocesos, así como configurar un subproceso para ejecutar un solo bloque de código.
Cualquier desarrollador necesita sincronizar el trabajo con subprocesos durante las operaciones de lectura y escritura para los recursos que tienen varios subprocesos asignados.
Es importante que al momento de acceder al recurso tengas los datos actualizados para que otro hilo pueda cambiarlo y obtengas la información más actualizada. Aunque tomemos el ejemplo de una cuenta bancaria, hasta que no haya llegado el dinero a ella no se puede utilizar, por lo que es importante tener siempre los datos actualizados. Java tiene clases especiales para sincronizar y administrar hilos.
Objetos de hilo
Todo comienza con el hilo principal (main), es decir, al menos su programa ya tiene un hilo en ejecución. El subproceso principal puede crear otros subprocesos mediante Callable o Runnable . La creación difiere solo en el resultado devuelto, Runnable no devuelve un resultado y no puede generar una excepción comprobada. Por lo tanto, tiene una buena oportunidad para crear un trabajo eficiente con archivos, pero esto es muy peligroso y debe tener cuidado.
También es posible programar la ejecución de subprocesos en un núcleo de CPU separado. El sistema puede moverse fácilmente entre hilos y ejecutar un hilo específico con la configuración adecuada: es decir, el hilo que lee los datos se ejecuta primero, tan pronto como tenemos datos, luego se los pasamos al hilo que se encarga de la validación, después de eso, lo pasamos al subproceso para ejecutar algo de lógica comercial y un nuevo subproceso los escribe de nuevo. En tal situación, 4 subprocesos están procesando datos a la vez y todo funcionará más rápido que un subproceso. Cada flujo de este tipo se convierte en un flujo de sistema operativo nativo, pero la forma en que se convertirá depende de la implementación de JVM.
La clase Thread se utiliza para crear y trabajar con hilos. Tiene mecanismos de control estándar, así como abstractos, como clases y colecciones de java.util.concurrent .
Sincronización de subprocesos en Java
La comunicación se proporciona compartiendo el acceso a los objetos. Esto es muy efectivo, pero al mismo tiempo es muy fácil cometer un error al trabajar. Los errores se presentan en dos casos: interferencia de subprocesos: cuando otro subproceso interfiere con su subproceso, y errores de consistencia de la memoria: consistencia de la memoria. Para solucionar y prevenir estos errores, disponemos de diferentes métodos de sincronización.
La sincronización de subprocesos en Java es manejada por monitores, este es un mecanismo de alto nivel que permite que solo un subproceso ejecute un bloque de código protegido por el mismo monitor a la vez. El comportamiento de los monitores se considera en términos de bloqueos; un monitor - una cerradura.
La sincronización tiene varios puntos importantes a los que debe prestar atención. El primer punto es la exclusión mutua: solo un subproceso puede poseer el monitor, por lo que la sincronización en el monitor implica que una vez que un subproceso ingresa a un bloque sincronizado protegido por el monitor, ningún otro subproceso puede ingresar al bloque protegido por este monitor hasta que el primer hilo sale del bloque sincronizado. Es decir, varios subprocesos no pueden acceder al mismo bloque sincronizado al mismo tiempo.
Pero la sincronización no es sólo exclusión mutua. La sincronización garantiza que los datos escritos en la memoria antes o dentro de un bloque sincronizado sean visibles para otros subprocesos que están sincronizados en el mismo monitor. Después de salir del bloque, liberamos el monitor y otro hilo puede tomarlo y comenzar a ejecutar este bloque de código.
Cuando un nuevo subproceso captura el monitor, obtenemos acceso y la capacidad de ejecutar ese bloque de código, y en ese momento las variables se cargarán desde la memoria principal. Luego podemos ver todas las entradas visibles por la versión anterior del monitor.
Una lectura o escritura individual de un campo es atómica si el campo está declarado como volatile (esto es importante para long y double) o si todos los accesos a él están protegidos por el mismo bloqueo. Si no hay sincronización, pueden producirse efectos de reordenamiento (reordering): en un programa sincronizado incorrectamente, un subproceso puede ver los cambios hechos por otro subproceso en un orden inesperado.
El efecto de exclusión mutua y sincronización de subprocesos, es decir, su funcionamiento correcto se logra solo al ingresar un bloque o método sincronizado que implícitamente adquiere un bloqueo, o al obtener explícitamente un bloqueo. Hablaremos de ello a continuación. Ambas formas de trabajar afectan tu memoria y es importante no olvidarse de trabajar con variables volátiles .
Campos volátiles en Java
La palabra clave volatile resuelve el problema de la visibilidad. Si un subproceso escribió un valor en un campo volatile, cualquier otro subproceso que lea ese campo después verá el valor nuevo y no una copia obsoleta.
En cuanto a su efecto sobre la memoria, la escritura en un campo volatile se parece a la liberación de un monitor, y la lectura, a su adquisición. Está garantizado que el subproceso B verá todo lo que el subproceso A escribió antes de escribir en el campo volatile, si B leyó de ese campo el valor escrito. Esta relación se llama happens-before («ocurre antes»).
Una lectura o escritura individual de un campo volatile es atómica, también para long y double. En cambio, las operaciones compuestas, como count++ (leer, incrementar, escribir), no lo son: entre los pasos puede colarse otro subproceso, y parte de los incrementos se pierde. La exclusión mutua no la proporciona volatile. Para que una operación compuesta se ejecute de forma atómica, se necesitan synchronized, Lock o clases como AtomicInteger, AtomicLong, etc.
Java concurrente
Si desea crear una aplicación súper eficiente y de subprocesos múltiples, debe usar las clases de la biblioteca JavaConcurrent , que se encuentran en el paquete java.util.concurrent .
La biblioteca es muy voluminosa y tiene una funcionalidad diferente, así que echemos un vistazo a lo que hay dentro y dividámoslo en algunos módulos:
Colecciones concurrentes es un conjunto de colecciones para trabajar en un entorno de subprocesos múltiples. En lugar del envoltorio básico Collections.synchronizedList con bloqueo de acceso a toda la colección, se utilizan bloqueos en segmentos de datos o algoritmos de espera para leer datos en paralelo.
Colas : colas bloqueantes y no bloqueantes para trabajar en un entorno de subprocesos múltiples. Las colas sin bloqueo se centran en la velocidad y el funcionamiento sin bloquear subprocesos. Las colas de bloqueo son adecuadas para el trabajo cuando necesita "ralentizar" los subprocesos del productor o del consumidor . Por ejemplo, en una situación en la que no se cumplen algunas de las condiciones, la cola está vacía o llena, o no hay un Consumidor libre 'a.
Los sincronizadores son utilidades de utilidad para sincronizar subprocesos. Son un arma poderosa en la computación "paralela".
Executors es un marco para la creación más conveniente y fácil de grupos de subprocesos, es fácil configurar la programación de tareas asincrónicas con la obtención de resultados.
Los bloqueos son muchos mecanismos de sincronización de subprocesos flexibles en comparación con el básico sincronizado , esperar , notificar , notificar a todos .
Los atómicos son clases que pueden admitir operaciones atómicas en primitivas y referencias.
GO TO FULL VERSION