Entrevistador, no cuelgues el teléfono, Sincronizado, todavía puedo hablar durante media hora.
La palabra clave sincronizada se usa a menudo para la sincronización de subprocesos. El hilo que ejecuta el bloque de código sincronizado modificado por Synchronized primero obtendrá el "bloqueo de objeto". Si otro hilo intenta ejecutar el bloque de código sincronizado, se bloqueará hasta que el hilo termine de ejecutar el código de sincronización y libere el "bloqueo de objeto". El concepto anterior ciertamente no es desconocido, pero cuál es el bloqueo de objeto específico, tal vez no esté muy claro, este artículo comienza a partir de sus principios subyacentes e interpreta la palabra clave sincronizada en detalle
Concepto de palabra clave sincronizada
Sincronizado es una palabra clave en Java, es usar el mecanismo de bloqueo para lograr la sincronización, el mecanismo de bloqueo tiene las siguientes dos características
-
Exclusión mutua (también llamada atomicidad) : solo un hilo puede adquirir un bloqueo de objeto a la vez
-
Visibilidad : después de que el subproceso adquiere el bloqueo del objeto, borrará las variables compartidas utilizadas por los bloques de código sincronizado en la memoria de trabajo del subproceso
Antes de que el objeto libere el bloqueo, actualizará la modificación de la variable compartida en la memoria de trabajo del hilo de nuevo a la memoria principal
Bloqueo de objeto y bloqueo de clase
En Java, cada objeto tendrá un monitor
objeto, el objeto monitor se dice a menudo, se bloquea o se llama objeto
Monitor de bloqueo . Los bloqueos de clase (similar al uso de Sincronizado (Object.class)) se implementan realmente a través de bloqueos de objetos. Si está familiarizado con el mecanismo de carga de clases, debe saber que cuando se carga una clase, se agregará un objeto de Clase al montón. El objeto de monitor de este objeto de clase es el bloqueo de clase, que muestra que solo hay un bloqueo de clase para cada clase
Encabezado de objeto
El diseño de los objetos Java almacenados en la memoria se puede dividir en tres áreas: encabezado de objeto, datos de instancia y relleno de alineación.
El encabezado del objeto se divide en dos partes: la primera parte almacena los datos de tiempo de ejecución del objeto en sí, también conocido como Mark Word. Las máquinas virtuales de 32 bits ocupan 32 bits y las máquinas virtuales de 64 bits ocupan 64 bits. Como se muestra en la figura, bajo diferentes estados de bloqueo, la estructura de Mark Word. El puntero de la clase de almacenamiento de la segunda parte
Principio de bloqueo de objeto (monitor)
En la máquina virtual Java, ObjectMonitor implementa el monitor, la estructura de datos principal es la siguiente
1 ObjectMonitor() {
2 _header = NULL;//markOop对象头
3 _count = 0;
4 _waiters = 0,//等待线程数
5 _recursions = 0; //重入次数
6 _object = NULL;
7 _owner = NULL;//指向获得ObjectMonitor对象的线程
8 _WaitSet = NULL;//处于wait状态的线程,会被加入到wait set;
9 _WaitSetLock = 0 ;
10 _Responsible = NULL ;
11 _succ = NULL ;
12 _cxq = NULL ;
13 FreeNext = NULL ;
14 _EntryList = NULL ;//处于等待锁block状态的线程,会被加入到entry set;
15 _SpinFreq = 0 ;
16 _SpinClock = 0 ;
17 OwnerIsThread = 0 ;// _owner is (Thread *) vs SP/BasicLock
18 _previous_owner_tid = 0;// 监视器前一个拥有者线程的ID
19 }
ObjectMonitor tiene principalmente varias variables miembro que necesitan atención.
- _owner: apunta al hilo que obtuvo el objeto ObjectMonitor
- _EntryList: los subprocesos en el estado de espera de bloqueo se agregarán al conjunto de entradas
- _WiatSet: el subproceso en el estado de espera se agregará al conjunto de espera (llame al método de espera del objeto de sincronización)
Múltiples subprocesos tienen acceso al mismo tiempo un cierto código de sincronización, entrará en la primera _EntryList
serie fueron bloqueados a la espera de, cuando el objeto hilo es conseguir monitor
de nuevo en la región propietario, y para monitor
los _owner
puntos variables al hilo, mientras que monitor
el contador de count
auto más uno, si el subproceso de llamada el objeto de sincronización wait()
métodos actualmente en cartera se libera monitor
, _owner
la variable se pone a cero null
, count
ya que una reducción, mientras que el hilo entra en _WaitSet
la espera de despertador, el hilo ha terminado el bloque de sincronización, también _Owner
y count
restablecer variables.
Proceso de desbloqueo de bloqueo de bloque de código
public class SyncCodeBlock{
public int i;
public void syncTask(){
synchronized(this) {
i++;
}
}
}
Después de la descompilación, obtenga el código de bytes del código anterior
public void syncTask();
Code:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: dup
6: getfield #2 // Field i:I
9: iconst_1
10: iadd
11: putfield #2 // Field i:I
14: aload_1
15: monitorexit
16: goto 24
19: astore_2
20: aload_1
21: monitorexit
22: aload_2
23: athrow
24: return
Como se puede ver en el código de bytes, se utiliza la capa inferior de Sincronizado monitorenter
y las monitorexit
instrucciones implementan la sincronización de subprocesos.
-
Cuando
monitorenter
se ejecuta la instrucción, el subproceso intentará adquirir el bloqueo del objeto de sincronización (es decir, el objeto del monitor) .Si la variable de recuento del monitor (registrando el número de veces que ingresa el subproceso) es 0, establezca el recuento en 1 y _propietario del subproceso actual ''. Éxito. Si el hilo ya posee el bloqueo del objeto, puede volver a ingresarlo. El valor de la calculadora del contador también aumentará en uno al volver a ingresar -
Al señalar la
monitorexit
instrucción, el ejecutor disminuye el contador de conteo en uno. Cuando el contador es 0, otros hilos tendrán la oportunidad de retenerVale la pena señalar que el compilador se asegurará de que no importa cómo se complete el método, cada
monitorenter
instrucción invocada en el método ejecutará lamonitorexit
instrucción correspondiente , ya sea que el método finalice de manera normal o anormal.Si el método se completa anormalmente, el compilador generará automáticamente un controlador de excepciones. El propósito del controlador de excepciones es ejecutar la
monitorexit
instrucción. También se puede ver en el código de bytes que hay unamonitorexit
instrucción adicional . Es el monitor de liberación que se ejecuta cuando finaliza la excepción. Instrucción.
Método de bloqueo del cuerpo y proceso de desbloqueo
public class Main{
public int i;
public synchronized void syncTask(){
i++;
}
}
Después de descompilar el código anterior, obtienes el siguiente código de bytes
public synchronized void syncTask();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: dup
2: getfield #2 // Field i:I
5: iconst_1
6: iadd
7: putfield #2 // Field i:I
10: return
La sincronización del cuerpo del método es implícita, es decir, no se requieren instrucciones de código de bytes para controlar. La JVM puede ACC_SYNCHRONIZED
distinguir si un método está sincronizado desde el indicador de acceso en la estructura de la tabla de métodos (estructura method_info) en el grupo constante de métodos .
Al llamar a un método, verifique ACC_SYNCHRONIZED
si el método está configurado. Si está configurado, el hilo necesita sostener primero el monitor del objeto, luego ejecutar el método y finalmente liberar el monitor cuando el método se haya completado.
Si se lanza una excepción durante la ejecución de un método síncrono, y la excepción no se puede manejar dentro del método, el monitor en poder del método síncrono se liberará automáticamente cuando la excepción se arroje fuera del método síncrono.