Actualización de bloqueos sincronizados en java (bloqueos sesgados, bloqueos ligeros y bloqueos pesados)

Puntos de conocimiento previo del bloqueo de sincronización de Java

  1. Si usa bloqueos en la codificación, puede usar la palabra clave sincronizada para bloquear métodos y bloques de código sincrónicamente
  2. El bloqueo de sincronización sincronizada es un bloqueo implícito integrado de jvm (relativo a Lock, bloqueo y liberación implícitos)
  3. La realización del bloqueo de sincronización sincronizada depende del sistema operativo. Adquirir y liberar el bloqueo para las llamadas al sistema hará que el modo de usuario y el modo de kernel cambien.
  4. Antes de jdk1.5, el bloqueo solo se puede usar sincronizado, 1.6 introduce el bloqueo de sincronización de bloqueo (basado en la implementación de Java, bloqueo y liberación explícitos, mejor rendimiento)
  5. jdk1.6 presenta el concepto de bloqueo sesgado, bloqueo ligero y bloqueo pesado para el bloqueo de sincronización sincronizado (de hecho, optimiza el rendimiento de sincronizado y minimiza el cambio de contexto causado por la competencia de bloqueo)
  6. Ya sea que use sincronizado o bloqueo, el cambio de contexto de hilo es inevitable
  7. Una de las optimizaciones de rendimiento de Lock en relación con el sincronizado es: cuando el hilo está bloqueado, Lock adquiriendo el bloqueo no provocará el cambio entre el modo de usuario y el modo de kernel, mientras que el sincronizado lo hará (ver punto 3). Pero el bloqueo de subprocesos conducirá a un cambio de contexto (consulte el punto 6)
  8. El bloqueo y el despertar de los subprocesos de Java dependen de las llamadas al sistema operativo, lo que resulta en un cambio entre el modo de usuario y el modo de kernel.
  9. El cambio entre el modo de usuario y el modo de kernel mencionado anteriormente es un cambio de contexto de proceso en lugar de un cambio de contexto de hilo.

Este artículo se centra en la actualización de bloqueos sincronizados.

bloqueo de sincronización sincronizada

encabezado de objeto java

Cada objeto java tiene un encabezado de objeto, que se compone de un puntero de tipo y un campo de etiqueta. En una máquina virtual de 64 bits, el puntero comprimido no está activado, el campo de etiqueta ocupa 64 bits y el puntero de tipo ocupa 64 bits, totalizando 16 bytes. La información del tipo de bloqueo son los 2 últimos dígitos del campo de etiqueta: 00 significa bloqueo ligero, 01 significa sin bloqueo o bloqueo sesgado, 10 significa bloqueo pesado; si los 3 dígitos inferiores son 1, significa que este tipo de bloqueo sesgado está activado , que es 0 Indica que el bloqueo de polarización de la clase está deshabilitado. Como se muestra en la imagen a continuación, la imagen proviene de wiki: https://wiki.openjdk.java.net/display/HotSpot/Synchronization

La columna de la izquierda indica que el bloqueo de polarización está habilitado (cuadro 1) y la columna de la derecha indica que el bloqueo de polarización está inhabilitado (cuadro 3). 1 y 3 representan el estado inicial de no bloqueo. Si el bloqueo de polarización está habilitado, el paso de actualización de bloqueo debe ser 1-> 2-> 4-> 5, si el bloqueo de polarización está desactivado, el paso de actualización de bloqueo es 3- > 4-> 5.

Usé jdk8. Imprimí los parámetros y los miré. De forma predeterminada, el bloqueo de polarización está habilitado. Si está deshabilitado: -XX:-UseBiasedLocking

Hay varios otros parámetros sobre el bloqueo de polarización:

Preste atención al parámetro BiasedLockingStartupDelay, el valor predeterminado es 4000 ms, lo que significa que la máquina virtual se inicia con un retraso de 4 s antes de usar el bloqueo sesgado (use primero el bloqueo ligero).

Bloqueo de sesgo

El escenario de procesamiento de bloqueo sesgado es que la mayoría de las veces, solo el mismo subproceso solicita un bloqueo, y no hay una situación en la que varios subprocesos compitan por bloqueos. Mire el cuadro rojo 2 del encabezado del objeto, hay un campo de ID de subproceso: cuando el subproceso se bloquea por primera vez, jvm establece la dirección del subproceso actual en el indicador de ID de subproceso a través de cas, y los últimos 3 bits son 101. La próxima vez que el mismo hilo adquiera el bloqueo, solo verifique si los últimos 3 dígitos son 101, si es el hilo actual, si la época es igual a la época de la clase de objeto de bloqueo (la wiki dice que no hay configuración de cas nuevamente para la optimización actual del multiprocesador del funcionamiento de cas).

La mejora del rendimiento aportada por la optimización del bloqueo de polarización se refiere a evitar el cambio de modo de usuario y modo de kernel causado por la llamada al sistema del bloqueo, porque el mismo hilo obtiene el bloqueo y no hay necesidad de realizar una llamada al sistema cada vez. se adquiere la cerradura.

Si el ID del subproceso no coincide con el subproceso actual cuando el subproceso actual adquiere el bloqueo (en el estado desbloqueado), el bloqueo sesgado se revocará y se sesgará nuevamente al subproceso actual. Si el número de veces alcanza el valor de BiasedLockingBulkRebiasThreshold, el el valor predeterminado es 20 veces, y el bloqueo sesgado de la clase actual deja de ser válido. El impacto es que el valor de época cambia, el valor de época de la clase bloqueada aumenta en 1 y el objeto de bloqueo posterior volverá a copiar el valor de época de la clase al bit de marca de época en la figura. Si el número total de revocaciones alcanza el valor de BiasedLockingBulkRevokeThreshold (40 veces de forma predeterminada), el bloqueo sesgado de la clase actual está deshabilitado, que es la columna en el lado derecho del encabezado del objeto, y el bloqueo comienza directamente desde el bloqueo ligero. (la cerradura se actualiza).

La revocación de bloqueos sesgados es un proceso muy problemático. Requiere que todos los subprocesos alcancen un punto seguro (se produce STW), atraviesen las pilas de subprocesos de todos los subprocesos para comprobar si el objeto de bloqueo se mantiene, para evitar la pérdida de bloqueos y para hacer frente a épocas .

Si hay competencia de subprocesos múltiples, el bloqueo de sesgo también ha comenzado a escalar.

Cerradura ligera

El escenario del procesamiento de bloqueo ligero es que diferentes subprocesos solicitan bloqueos en el mismo período de tiempo (los subprocesos se ejecutan alternativamente). Incluso si hay varios subprocesos compitiendo por bloqueos al mismo período de tiempo, el subproceso que adquirió el bloqueo mantiene el bloqueo durante un tiempo muy corto y el bloqueo se libera pronto.

Cuando el hilo está bloqueado, se considera que no es un bloqueo pesado y se abre un espacio en la pila de subprocesos actual. Como Li Xiaoran, copie el campo de etiqueta del encabezado del objeto de bloqueo (copiar es para hacer un registro, porque el encabezado del objeto de bloqueo se copiará más tarde). El valor del campo de etiqueta se reemplaza con la dirección de espacio del campo de etiqueta recién copiado, al igual que el puntero para bloquear parte del registro de la imagen en el encabezado del objeto. bits, debido a la alineación de la memoria, es 00). Luego, según la operación CAS, la dirección del campo de etiqueta copiado se establece en el valor del bit de etiqueta del encabezado del objeto de bloqueo. Si tiene éxito, se adquiere el bloqueo. Si se considera que no es un candado pesado cuando el candado está bloqueado, y los dos últimos dígitos no son 01 (provenientes de un candado sesgado o un estado sin candado), significa que el hilo ya lo ha retenido. Si el hilo actual está en (se requiere reentrante), luego establezca un 0, aquí hay una estructura de pila, simplemente presione un 0 directamente. Cuando finalmente se libera el bloqueo, la pila se abre y el último elemento registra el valor del campo de etiqueta original del objeto de bloqueo, que luego se establece en el encabezado del objeto de bloqueo a través de CAS.

Tenga en cuenta que al adquirir el bloqueo, cas falla, el hilo actual girará por un tiempo, alcanzando un cierto número de veces, actualice al bloqueo de peso pesado, el hilo actual también se bloqueará.

Bloqueo de peso pesado

El peso pesado es lo que generalmente llamamos el bloqueo de sincronización agregado, que es la implementación de bloqueos basados ​​en Java. Las llamadas al sistema se deben realizar al adquirir y liberar bloqueos, lo que conduce al cambio de contexto.

Acerca del bloqueo de giro

Con respecto al bloqueo de giro, tengo dos explicaciones principales: 1. Es un candado liviano que no puede competir y no se expandirá inmediatamente a un peso pesado, sino que girará un cierto número de veces para intentar adquirir el bloqueo; es un bloqueo de peso pesado. Si la competencia falla, no se bloqueará de inmediato y girará un cierto número de veces (aquí está involucrado un algoritmo de autoajuste). Con respecto a esta descripción, todavía depende de la implementación de la fuente de jvm para determinar cuál es verdadera: http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/tip/src/share/vm/runtime/synchronizer .cpp

Parámetros del bloqueo de sesgo de impresión

como sigue:

-XX: + UnlockDiagnosticVMOptions
-XX: + PrintBiasedLockingStatistics

Adquirí el mismo bloqueo en el ciclo del método principal y el resultado de la impresión es el siguiente:

    public static void main(String[] args) {
        int num = 0;
        for (int i = 0; i < 1_000_000000; i++) {
            synchronized (lock) {
                num++;
            }
        }
    }

Supongo que te gusta

Origin blog.csdn.net/x763795151/article/details/115059756
Recomendado
Clasificación