Prefacio
Cualquiera que esté familiarizado con la programación concurrente de Java sabe que la regla de sucesión antes (HB) en JMM (modelo de memoria Java), que define el orden y la visibilidad de las operaciones de múltiples subconjuntos de Java, evitando el impacto del reordenador del compilador en los resultados del programa.
Hay una regla de "cuando antes" en el idioma Java. Es una relación de orden parcial entre dos operaciones definidas en el modelo de memoria Java. Si la operación A ocurre primero en la Operación B, significa que antes de que ocurra la operación B, el impacto de la operación A puede observarse mediante la Operación B. La "influencia" incluye modificar el valor de las variables compartidas en la memoria, enviar mensajes, métodos de llamadas, etc., que básicamente no tiene relación con la secuencia de ocurrencia en el tiempo. Este principio es particularmente importante. Es la base principal para juzgar si hay competencia en los datos y si los hilos son seguros.
Según la declaración oficial:
Cuando una variable se lee mediante múltiples hilos y escrita por al menos un hilo, si no hay una relación HB entre las operaciones de lectura y escritura, surgen problemas de carrera de datos.
Para garantizar que el hilo que funciona B ve el resultado de la Operación A (independientemente de si A y B están en el mismo hilo), el principio de HB debe cumplirse entre A y B, y si no, puede conducir a reordenar.
Cuando falta la relación HB, pueden ocurrir problemas de reordenamiento.
¿Cuáles son las reglas para HB?
Todos están muy familiarizados con esto. Se introducirán la mayoría de los libros y artículos. Vamos a revisarlo brevemente aquí:
Entre ellos, he audado las reglas de entrega, lo cual es crucial. Cómo usar las reglas de entrega de manera competente es la clave para lograr la sincronización.
Luego, explique HB desde otra perspectiva: cuando una operación A HB opera B, entonces el resultado de la operación de la operación A en la variable compartida es visible para la operación B.
Al mismo tiempo, si la operación B Hb opera C, entonces el resultado de la operación de la operación A en la variable compartida es visible para la operación B.
El principio de lograr la visibilidad es el protocolo de caché y la barrera de memoria. La visibilidad se logra a través de protocolos de coherencia de caché y barreras de memoria.
¿Cómo lograr la sincronización?
En el libro de Doug Lea "Java Concurrencia en la práctica", la siguiente descripción es:
El libro menciona: al combinar algunas reglas de HB, se puede lograr la visibilidad de una variable protegida desbloqueada.
Pero debido a que esta técnica es sensible al orden de las declaraciones, es propensa a los errores.
A continuación, el autor demostrará cómo sincronizar una variable a través de reglas volátiles y reglas de orden de programa.
Tengamos un ejemplo familiar:
clase ThreadPrintDemo {static int num = 0; bandera booleana volátil estática = falso; public static void main (string [] args) {Thread t1 = new Thread (() -> {for (; 100> num;) {if (! flag && (num == 0 || ++ num % 2 == 0)) {system.out.println (num); flag = true;}}}); Thread t2 = new Thread (() -> {for (; 100> num;) {if (flag && (++ num % 2! = 0)) {system.out.println (num); flag = false;}}}); t1.start (); t2.start (); }}El propósito de este código es imprimir los números 0 - 100 entre dos hilos.
Los estudiantes que están familiarizados con la programación concurrente deben decir que esta variable NUM no usa volátiles, y habrá problemas de visibilidad, es decir, el hilo T1 tiene NUM actualizado y el hilo T2 no puede percibirlo.
Jaja, pensó el autor al principio, pero recientemente al estudiar las reglas de HB, descubrí que está bien eliminar la modificación volátil de NUM.
Vamos a analizarlo y el póster dibujó una imagen:
Analicemos esta figura:
Nota: La regla HB asegura que los resultados de la operación anterior sean visibles para la siguiente operación.
Por lo tanto, en el applet anterior, el hilo B es plenamente consciente de la modificación de NUM por subproceso A - incluso si NUM no se modifica con volátil.
De esta manera, utilizamos el principio de HB para realizar el funcionamiento sincrónico de una variable, es decir, en un entorno de múltiples subprocesos, garantizamos la seguridad de la modificación concurrente de las variables compartidas. Y no hay primitivas Java para esta variable: volátil y sincronizada y CAS (suponiendo que cuenta).
Esto puede parecer inseguro (realmente seguro) y puede no parecer fácil de entender. Porque todo esto es implementado por el protocolo de caché y la barrera de memoria en el HB subyacente.
Otras reglas para lograr la sincronización
Implementación utilizando reglas de terminación de hilos:
static int a = 1; public static void main (string [] args) {Thread tb = new Thread (() -> {a = 2;}); Thread ta = new Thread (() -> {try {tb.Join ();} capt (interruptedException e) {// no} system.out.println (a);}); ta.Start (); tb.start (); } Utilice las reglas de inicio de hilo para implementar:
static int a = 1; public static void main (string [] args) {Thread tb = new Thread (() -> {System.out.println (a);}); Hilo ta = new Thread (() -> {tb.start (); a = 2;}); ta.Start (); }Estas dos operaciones también pueden garantizar la visibilidad de la variable a.
Realmente subvierte el concepto anterior. En el concepto anterior, si una variable no se modifica por volátil o final, entonces su lectura y escritura bajo múltiples subprocesos es definitivamente inseguro, porque habrá cachés, lo que resulta en cuya lectura no es la última.
Sin embargo, al usar HB, podemos lograrlo.
Resumir
Aunque el título de este artículo es realizar operaciones síncronas de variables compartidas a través de la sucesión antes, el objetivo principal es comprender el sucesión antes de profundidad. Comprender su concepto de sucesión antes es garantizar el orden de la operación anterior a la siguiente operación y la visibilidad de la operación da como resultado un entorno múltiple.
Al mismo tiempo, mediante el uso de reglas transitivas de manera flexible y luego combinando reglas, se pueden sincronizar dos hilos: la implementación de la variable compartida especificada sin usar primitivas también puede garantizar la visibilidad. Aunque esto no parece ser muy fácil de leer, también es un intento.
Doug Lea da práctica en JUC sobre cómo combinar reglas para lograr la sincronización.
Por ejemplo, la sincronización de la clase interna de la versión anterior de FutureTask (desapareció), modifique la variable volátil a través del método de transferencia de tryreleasas e TryAcquireshared lee la variable volátil, que utiliza las reglas volátiles;
Esto aprovecha las reglas de orden de programa estableciendo una variable de resultado no volátil antes de la transferencia de tryrelebras y luego leyendo la variable de resultado después de TryAcquireshared.
Esto garantiza la visibilidad de la variable de resultado. Similar a nuestro primer ejemplo: usar reglas de orden de programa y reglas volátiles para lograr una visibilidad variable normal.
El propio Doug Lea dijo que esta tecnología de "uso de ayuda" es muy propensa a los errores y debe usarse con precaución. Pero en algunos casos, este tipo de "apalancamiento" es muy razonable.
De hecho, Bloquingqueue también "usó" las reglas de sucesión antes. ¿Recuerdas la regla de desbloqueo? Cuando se produce el desbloqueo, los elementos internos deben ser visibles.
Hay otras operaciones en la biblioteca de clases que también "usan" el principio antes de los contenedores concurrentes, CountdownLatch, Semaphore, Future, Executor, CyclicBarrier, Interchanger, etc.
En resumen, en resumen:
El principio de sucesión antes es el núcleo de JMM. Solo cuando se cumpla el principio de HB puede garantizar el orden y la visibilidad, de lo contrario, el compilador reordenará el código. HB incluso define las reglas de bloqueo y volátil.
Mediante la combinación apropiada de las reglas de HB, se puede lograr el uso correcto de las variables compartidas ordinarias.
De acuerdo, lo anterior es todo el contenido de este artículo. Espero que el contenido de este artículo tenga cierto valor de referencia para el estudio o el trabajo de todos. Si tiene alguna pregunta, puede dejar un mensaje para comunicarse. Gracias por su apoyo a Wulin.com.