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-escritura en un campo es una operación atómica si el campo se declara volátil o está protegido por un bloqueo único adquirido antes de cualquier lectura-escritura. Pero si aún encuentra un error, obtendrá un error sobre el reordenamiento (cambiar el orden, reordenar). Se manifiesta en programas de subprocesos múltiples sincronizados incorrectamente, donde un subproceso puede observar los efectos producidos por otros subprocesos.

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

Si una variable está marcada como volátil , está disponible globalmente. Esto significa que si un subproceso accede a una variable volátil , obtendrá su valor antes de usar el valor del caché.

Una escritura funciona como una liberación de monitor y una lectura funciona como una captura de monitor. El acceso se realiza en una relación del tipo “realizado antes”. Si lo resuelve, todo lo que será visible para el subproceso A cuando acceda a una variable volátil es la variable para el subproceso B. Es decir, tiene la garantía de no perder los cambios de otros subprocesos.

Las variables volátiles son atómicas, es decir, al leer dicha variable, se utiliza el mismo efecto que al obtener un bloqueo: los datos en la memoria se declaran inválidos o incorrectos, y el valor de la variable volátil se lee nuevamente de la memoria . Al escribir, se usa el efecto en la memoria, así como al liberar un bloqueo: se escribe un campo volátil en la memoria.

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:

Java concurrente

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.