Para resolver los problemas de atomicidad, visibilidad y orden en la programación concurrente, el lenguaje Java proporciona una serie de palabras clave relacionadas con el procesamiento concurrente, como , , , synchronized
etc.volatile
final
concurren包
En "Comprensión profunda de la máquina virtual de Java", hay un pasaje de este tipo:
synchronized
Las palabras clave se pueden utilizar como una de las soluciones cuando se requieren las tres características de atomicidad, visibilidad y orden, y parece ser "universal". De hecho, la mayoría de las operaciones de control de concurrencia se pueden realizar mediante sincronizado.
Luego, este artículo gira en torno synchronized
a él, introduciendo principalmente synchronized
el uso, synchronized
los principios y synchronized
cómo brindar atomicidad, visibilidad y garantías de orden.
uso de sincronizado
synchronized
Es una palabra clave de control de concurrencia proporcionada por Java. Hay dos usos principales, que son métodos sincronizados y bloques de código sincronizados. Es decir, synchronized
se pueden decorar tanto los métodos como los bloques de código.
/**
* @author Hollis 18/08/04.
*/
public class SynchronizedDemo {
//同步方法
public synchronized void doSth(){
System.out.println("Hello World");
}
//同步代码块
public void doSth1(){
synchronized (SynchronizedDemo.class){
System.out.println("Hello World");
}
}
}
synchronized
Solo se puede acceder a los métodos y bloques de código modificados mediante un único subproceso a la vez.
El principio de realización de sincronizado.
synchronized
, es una palabra clave muy importante en Java para resolver el acceso de sincronización de datos en condiciones concurrentes. synchronized
Cuando queremos asegurarnos de que solo un subproceso acceda a un recurso compartido a la vez, podemos usar palabras clave para bloquear clases u objetos en el código .
En [Comprensión profunda de subprocesos múltiples (1) - Principio de implementación sincronizada] [2], he introducido su principio de implementación. Para garantizar la integridad del conocimiento, aquí hay una breve introducción. Para más detalles, lea el texto original.
Descompilamos el código anterior para obtener el siguiente código:
public synchronized void doSth();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello World
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
public void doSth1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: ldc #5 // class com/hollis/SynchronizedTest
2: dup
3: astore_1
4: monitorenter
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #3 // String Hello World
10: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: aload_1
14: monitorexit
15: goto 23
18: astore_2
19: aload_1
20: monitorexit
21: aload_2
22: athrow
23: return
Se puede ver en el código descompilado que: para los métodos de sincronización, JVM usa ACC_SYNCHRONIZED
marcadores para lograr la sincronización. Para bloques de código sincrónico. La JVM usa monitorenter
dos monitorexit
instrucciones para lograr la sincronización.
En [La especificación de la máquina virtual Java®][3], hay una introducción a los principios de implementación de los métodos de sincronización y los bloques de código de sincronización. Lo traduzco al chino de la siguiente manera:
La sincronización a nivel de método es implícita. Habrá una bandera en el grupo de constantes del método sincronizado
ACC_SYNCHRONIZED
. Cuando un subproceso quiere acceder a un método, verificará si está disponibleACC_SYNCHRONIZED
. Si está configurado, primero debe obtener el bloqueo del monitor, luego comenzar a ejecutar el método y liberar el bloqueo del monitor después de ejecutar el método. En este momento, si otros subprocesos solicitan ejecutar el método, se bloquearán porque no pueden obtener el bloqueo del monitor. Vale la pena señalar que si ocurre una excepción durante la ejecución del método y la excepción no se maneja dentro del método, el bloqueo del monitor se liberará automáticamente antes de que la excepción se lance fuera del método.Los bloques de código sincronizado se implementan usando
monitorenter
ymonitorexit
dos directivas. La ejecución demonitorenter
instrucciones puede entenderse como bloqueo y la ejecuciónmonitorexit
puede entenderse como liberación de bloqueos. Cada objeto mantiene un contador que registra el número de veces que ha sido bloqueado. El contador del objeto desbloqueado es 0. Cuando un subproceso adquiere el bloqueo (ejecuciónmonitorenter
), el contador se incrementa a 1. Cuando el mismo subproceso adquiere nuevamente el bloqueo del objeto, el contador se incrementa nuevamente. Cuando el mismo subproceso libera el bloqueo (ejecutamonitorexit
la instrucción), el contador vuelve a disminuir. cuando el contador es 0. El bloqueo se liberará y otros subprocesos podrán adquirir el bloqueo.
No importa si es ACC_SYNCHRONIZED
o monitorenter
, monitorexit
se implementa en base a Monitor. En la máquina virtual Java (HotSpot), Monitor se implementa en base a C++ e implementado por ObjectMonitor.
Se proporcionan varios métodos en la clase ObjectMonitor, como enter
, exit
, wait
, notify
, notifyAll
etc. sychronized
Al bloquear, se llamará al método de entrada de objectMonitor y se llamará al método de salida al desbloquear. (Para obtener detalles sobre Monitor, consulte [Comprensión profunda de subprocesos múltiples (4) - el principio de realización de Monitor][4])
sincronizado y atomicidad
La atomicidad significa que una operación es ininterrumpida y debe ejecutarse por completo, o no se ejecutará en absoluto.
¿Qué está pasando con nuestro problema de subprocesos múltiples en [la programación concurrente de Java? ] [5] analizado: el subproceso es la unidad básica de programación de la CPU. La CPU tiene el concepto de intervalos de tiempo y realizará la programación de subprocesos de acuerdo con diferentes algoritmos de programación. Cuando un subproceso comienza a ejecutarse después de obtener el intervalo de tiempo, después de que se agote el intervalo de tiempo, perderá el derecho a usar la CPU. Por lo tanto, en un escenario de subprocesos múltiples, dado que los intervalos de tiempo se rotan entre subprocesos, se producirán problemas de atomicidad.
En Java, para garantizar la atomicidad, se proporcionan dos instrucciones de código de bytes de alto monitorenter
nivel monitorexit
. Como se mencionó anteriormente, estas dos instrucciones de código de bytes son las palabras clave correspondientes en Java synchronized
.
A través monitorenter
de la instrucción y monitorexit
, se puede garantizar que synchronized
solo se puede acceder al código modificado por un subproceso a la vez, y no se puede acceder a otros subprocesos hasta que se libere el bloqueo. Por lo tanto, se puede usar en Java synchronized
para garantizar que las operaciones dentro de los métodos y bloques de código sean atómicas.
Cuando el subproceso 1 ejecuta
monitorenter
la instrucción, bloqueará el monitor.Después del bloqueo, otros subprocesos no pueden obtener el bloqueo a menos que el subproceso 1 lo desbloquee activamente. Incluso durante la ejecución, por algún motivo, como que se agote el intervalo de tiempo de la CPU, el subproceso 1 entrega la CPU, pero no la desbloquea. Y debido a quesynchronized
el bloqueo es reentrante, la próxima porción de tiempo solo puede ser adquirida por él mismo, y el código continuará ejecutándose. hasta que se ejecuten todos los códigos. Esto garantiza la atomicidad.
sincronizado y visibilidad
La visibilidad significa que cuando varios subprocesos acceden a la misma variable, un subproceso modifica el valor de la variable y otros subprocesos pueden ver inmediatamente el valor modificado.
Estamos en [Si alguien te pregunta cuál es el modelo de memoria de Java, envíale este artículo. ][1] analizado: El modelo de memoria de Java estipula que todas las variables se almacenan en la memoria principal, y cada subproceso también tiene su propia memoria de trabajo.La memoria de trabajo del subproceso almacena la memoria principal de las variables utilizadas en el subproceso.Memoria copy copy, todas las operaciones sobre variables por hilos deben realizarse en la memoria de trabajo y no pueden leer y escribir directamente en la memoria principal. Diferentes subprocesos no pueden acceder directamente a las variables en la memoria de trabajo de cada uno, y la transferencia de variables entre subprocesos requiere la sincronización de datos entre su propia memoria de trabajo y la memoria principal. Por lo tanto, puede suceder que el hilo 1 cambie el valor de una variable, pero el hilo 2 no sea visible.
Como mencionamos anteriormente, synchronized
el código modificado se bloqueará cuando comience a ejecutarse y se desbloqueará una vez que se complete la ejecución. Para garantizar la visibilidad, existe una regla como esta: antes de desbloquear una variable, la variable debe sincronizarse con la memoria principal. Después de desbloquear de esta manera, los subprocesos posteriores pueden acceder al valor modificado.
Por lo tanto, el valor del objeto bloqueado por la palabra clave sincronizada es visible.
sincronizado y ordenado
Secuencia significa que el orden de ejecución del programa se ejecuta en el orden del código.
Estamos en [Si alguien te pregunta cuál es el modelo de memoria de Java, envíale este artículo. ][1] analizado: además de la introducción de intervalos de tiempo, debido a la optimización del procesador y la reorganización de instrucciones, la CPU también puede ejecutar el código de entrada fuera de orden, como cargar-> agregar-> guardar puede optimizarse para cargar: >guardar->agregar. Aquí es donde puede haber un problema ordenado.
Cabe señalar aquí que synchronized
no se puede prohibir el reordenamiento de instrucciones y la optimización del procesador. Es decir, synchronized
los problemas antes mencionados no se pueden evitar.
Entonces, ¿por qué dice que synchronized
también se brindan garantías de pedido?
Se trata de ampliar el concepto de orden. El orden natural en los programas de Java se puede resumir en una oración: si se observa en este hilo, todas las operaciones se ordenan de forma natural. Si un hilo observa a otro, todas las operaciones están desordenadas.
La oración anterior también es la oración original en "Comprensión profunda de la máquina virtual Java", pero ¿cómo entenderla? Zhou Zhiming no explicó en detalle. Aquí me expandiré brevemente, que en realidad está as-if-serial语义
relacionado con .
as-if-serial
Semántica significa: no importa cómo se reordene (compilador y procesador para mejorar el paralelismo), el resultado de la ejecución de un programa de un solo subproceso no se puede cambiar. No importa cómo se optimicen los compiladores y procesadores, as-if-serial
se debe respetar la semántica.
as-if-serial语义
No entraré en detalles aquí as-if-serial语义
. En pocas palabras, se garantiza que en un solo subproceso, existen ciertas restricciones en el reordenamiento de instrucciones, y siempre que el compilador y el procesador cumplan con esta semántica, entonces se puede considerar el programa de un solo subproceso. para ser ejecutado en orden de. Por supuesto, en realidad hay reordenamientos, pero no tenemos que preocuparnos por la interferencia de tales reordenamientos.
Entonces, debido synchronized
al código modificado, solo se puede acceder por el mismo hilo al mismo tiempo. Entonces es ejecución de un solo subproceso. Por lo tanto, se puede garantizar su orden.
sincronización y optimización de bloqueo
synchronized
El uso, principio y efecto en la programación concurrente presentado anteriormente . es una gran palabra clave para usar.
synchronized
De hecho, se implementa con la ayuda de Monitor. El método de objectMonitor se llamará cuando se bloquee enter
y el método se llamará cuando se desbloquee exit
. De hecho, solo antes de JDK 1.6, la implementación de sincronizado llamará directamente a la enter
suma de ObjectMonitor.Este exit
tipo de bloqueo se denomina bloqueo de peso pesado.
Por lo tanto, en JDK1.6, se han realizado muchas optimizaciones en los bloqueos, y luego están los bloqueos ligeros, los bloqueos sesgados, la eliminación de bloqueos, los bloqueos giratorios adaptativos y el engrosamiento de bloqueos (los bloqueos giratorios existen en 1.4, pero el valor predeterminado está desactivado , jdk1.6 está activado de forma predeterminada), estas operaciones son para compartir datos de manera más eficiente entre subprocesos y resolver problemas de competencia.
Para bloqueos giratorios, engrosamiento de bloqueos y eliminación de bloqueos, consulte [Comprensión profunda de subprocesos múltiples (5): tecnología de optimización de bloqueo para máquinas virtuales Java][6]. Presentaré un artículo por separado más adelante y lo publicaré exclusivamente en mi blog (http://www.hollishuang.com) y cuenta oficial (Hollis), así que estén atentos.
Bueno, con respecto a synchronized
las palabras clave, presentamos su uso, principio y cómo garantizar atomicidad, secuencia y visibilidad, al mismo tiempo que ampliamos la información y el pensamiento relacionado con la optimización de bloqueo.