Conceptos básicos de Java (3): una comprensión simple de la palabra clave sincronizada

1. bloque de código sincronizado modificado sincronizado

Utilice sincronizado para modificar el segmento de código como un bloqueo de sincronización. El código es el siguiente:

public class LockDemo {
    
    
    public Object object = new Object();

    public void show(){
    
    
        synchronized (object) {
    
    
            System.out.println(">>>>>>hello world!");
        }
    }

    public static void main(String[] args) {
    
    
        new LockDemo().show();
    }
}

Ejecute directamente el método main () para compilar la clase LockDemo, luego ubique la ruta del archivo de clase LockDemo.class específica y javapvea el código subyacente a través del comando, de la siguiente manera:

javap -c LockDemo.class

Insertar descripción de la imagen aquí

De la figura anterior, encontramos que la capa inferior es implementada por monitorenter y monitorexit para implementar operaciones de sincronización.

Pero aquí encontrará que un monitorenter corresponde a dos monitorexits. Lógicamente hablando, ¿bloquear y desbloquear no deberían ser dos operaciones? ¿Por qué hay dos desbloqueos? De hecho, este diseño es para garantizar que el bloqueo se pueda liberar cuando ocurre una excepción y que el programa no se bloquee. Pero no es necesariamente 1:2, por ejemplo, el siguiente código:

public class LockDemo {
    
    
    public Object object = new Object();

    public void show(){
    
    
        synchronized (object) {
    
    
            System.out.println(">>>>>>hello world!");
            throw new RuntimeException(">>>>>>异常");
        }
    }

    public static void main(String[] args) {
    
    
        new LockDemo().show();
    }
}

Utilice el comando javap para ver el siguiente diagrama:

Insertar descripción de la imagen aquí

Lanza la excepción a la capa más externa a través de throwrow y finalmente monitorexit libera el bloqueo. El resumen es: los bloqueos sincronizados aparecen en pares, pero para poder liberar los bloqueos en circunstancias anormales, se agrega un buen procesamiento de cola.

2. Método de modificación sincronizado

El código se muestra a continuación:

public class LockDemo {
    
    
    public Object object = new Object();

    public void show(){
    
    
        synchronized (object) {
    
    
            synchronized (this) {
    
    
                System.out.println(">>>>>>hello world!");
            }
        }
    }

    public static synchronized void sop() {
    
    
    
    }

    public static void main(String[] args) {
    
    
        new LockDemo().show();
    }
}

aprobarjavap -v LockDemo.claseComando para ver el código de bytes, como se muestra a continuación:

Consejo : utilice java -v Xxx para ver información más completa

Insertar descripción de la imagen aquí

La sincronización a nivel de método es invisible y no necesita ser controlada mediante instrucciones de código de bytes, y se implementa en llamadas a métodos y operaciones de retorno. La máquina virtual puede saber si este método está declarado como un método sincrónico a partir del identificador del método ACC_SYNCHRONIZED en la estructura de la tabla de métodos en el grupo . Al llamar, la instrucción de llamada verificará si el identificador de acceso ACC_SYNCHRONIZED del método está configurado. Si Después de configurarlo, el hilo de ejecución debe mantener el monitor y luego ejecutar el método. Ningún otro hilo puede obtener el mismo monitor. Si un método sincronizado genera una excepción durante el tiempo de ejecución y no se puede manejar dentro del método, el monitor retenido por el método sincronizado se liberará automáticamente cuando la excepción se produzca fuera del límite del método sincronizado.Montior

3. ¿Qué es un monitor?

En la máquina virtual HotSpot, Monitor es un mecanismo utilizado para lograr la sincronización. Monitor puede garantizar que en un entorno de subprocesos múltiples, solo un subproceso pueda acceder a la sección crítica (recursos compartidos) al mismo tiempo, garantizando así la seguridad de los subprocesos.

En Java, cada objeto tiene un Monitor, y el Monitor del objeto se puede obtener mediante la palabra clave sincronizada. Cuando un hilo ingresa a un bloque sincronizado, intentará obtener el Monitor del objeto. Si el Monitor está retenido por otro hilo, el hilo actual se bloqueará hasta que el Monitor esté disponible. Cuando un subproceso sale del bloque sincronizado, libera el Monitor del objeto para que otros subprocesos puedan obtener el Monitor e ingresar al bloque sincronizado.

El siguiente es un ejemplo simple que demuestra cómo usar la palabra clave sincronizada para obtener el Monitor de un objeto para garantizar la seguridad de los subprocesos:

class Counter {
    
    
    private int count = 0;

    public synchronized void increment() {
    
    
        count++;
    }

    public synchronized void decrement() {
    
    
        count--;
    }

    public synchronized int getCount() {
    
    
        return count;
    }
}

class Main {
    
    
    public static void main(String[] args) {
    
    
        Counter counter = new Counter();

        // 创建 1000 个线程,每个线程分别执行 1000 次增加和减少操作
        for (int i = 0; i < 1000; i++) {
    
    
            new Thread(() -> {
    
    
                for (int j = 0; j < 1000; j++) {
    
    
                    counter.increment();
                    counter.decrement();
                }
            }).start();
        }

        // 等待所有线程执行完毕
        try {
    
    
            Thread.sleep(5000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }

        // 输出最终计数器的值
        System.out.println(counter.getCount()); // 0
    }
}

En el código anterior, creamos una clase llamada Counter, que contiene una variable de contador count y tres métodos sincronizados increment(), decrement() y getCount(). En estos métodos, utilizamos la palabra clave sincronizada para obtener el Monitor del objeto y garantizar que las operaciones en el contador sean seguras para subprocesos en un entorno de subprocesos múltiples.

En el método principal, creamos 1000 subprocesos, cada uno de los cuales realizó 1000 incrementos y disminuciones, lo que resultó en una gran cantidad de modificaciones simultáneas en el contador. Finalmente, generamos el valor del contador, esperando obtener 0 para asegurarnos de que todo se haya hecho correctamente.

La capa inferior de Monitor se implementa utilizando ObjectMonitor.cpp jvm, y cada objeto nace con un Monitor.

Consulte el método de construcción de inicialización de ObjectMonitor . El código fuente se muestra a continuación:

Insertar descripción de la imagen aquí

Atributos Descripción de propiedad
_dueño Apunta al hilo que contiene el objeto ObjectMonitor.
-EsperarEstablecer Almacenar la cola de subprocesos en estado de espera
_Lista de entradas Almacena la cola de subprocesos esperando el estado del bloque de bloqueo.
_recursiones Bloquear tiempos de reentrada
_contar Se utiliza para registrar la cantidad de veces que el hilo adquiere el bloqueo.

Insertar descripción de la imagen aquí

Si un subproceso A llega, ingresa a la colección EntryList y luego intenta adquirir el Monitor, está intentando adquirir el candado. Después de adquirir el candado, ingresará al área del Propietario, lo que significa que el subproceso A se ha bloqueado con éxito y luego Para llamar al código del método de sincronización. Supongamos que al ejecutar el método de sincronización, cuando se encuentra el método de espera, el subproceso A ingresará a la cola WaitSet y el Propietario en el Monitor también se volverá nulo, y el número de reentradas Recursiones = 0, luego el nuevo subproceso que acaba de llegar en EntryList o Los subprocesos que se despiertan en WaitSet comenzarán a competir por el bloqueo del Monitor. El proceso es el mismo que el bloqueo del hilo A.

Supongo que te gusta

Origin blog.csdn.net/qq_35971258/article/details/129143827
Recomendado
Clasificación