El uso de sincronizado en java, el principio de sincronizado y cómo sincronizado proporciona atomicidad, visibilidad y garantía de orden

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 , , , synchronizedetc.volatilefinalconcurren包

En "Comprensión profunda de la máquina virtual de Java", hay un pasaje de este tipo:

synchronizedLas 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 synchronizeda él, introduciendo principalmente synchronizedel uso, synchronizedlos principios y synchronizedcómo brindar atomicidad, visibilidad y garantías de orden.

uso de sincronizado

synchronizedEs 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, synchronizedse 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");
        }
    }
}

synchronizedSolo 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. synchronizedCuando 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_SYNCHRONIZEDmarcadores para lograr la sincronización. Para bloques de código sincrónico. La JVM usa monitorenterdos monitorexitinstrucciones 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á disponible ACC_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 monitorentery monitorexitdos directivas. La ejecución de monitorenterinstrucciones puede entenderse como bloqueo y la ejecución monitorexitpuede 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ón monitorenter), 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 (ejecuta monitorexitla 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_SYNCHRONIZEDo monitorenter, monitorexitse 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, notifyAlletc. sychronizedAl 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 monitorenternivel monitorexit. Como se mencionó anteriormente, estas dos instrucciones de código de bytes son las palabras clave correspondientes en Java synchronized.

A través monitorenterde la instrucción y monitorexit, se puede garantizar que synchronizedsolo 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 synchronizedpara garantizar que las operaciones dentro de los métodos y bloques de código sean atómicas.

Cuando el subproceso 1 ejecuta monitorenterla 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 que synchronizedel 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, synchronizedel 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 synchronizedno se puede prohibir el reordenamiento de instrucciones y la optimización del procesador. Es decir, synchronizedlos problemas antes mencionados no se pueden evitar.

Entonces, ¿por qué dice que synchronizedtambié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-serialSemá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-serialse 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 synchronizedal 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

synchronizedEl uso, principio y efecto en la programación concurrente presentado anteriormente . es una gran palabra clave para usar.

synchronizedDe hecho, se implementa con la ayuda de Monitor. El método de objectMonitor se llamará cuando se bloquee entery 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 entersuma de ObjectMonitor.Este exittipo 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 synchronizedlas 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.

Supongo que te gusta

Origin blog.csdn.net/zy_dreamer/article/details/132350503
Recomendado
Clasificación