Hable sobre la comprensión del hilo de Java (tres) -------- Sincronización de palabras clave

Hablando de la palabra clave sincronizada, todo el mundo está familiarizado con ella. Quizás rara vez use esta palabra clave en su código. Después de todo, el modo singleton ha sido encapsulado. Si hay un problema de concurrencia multiproceso, generalmente usa bloqueos distribuidos, porque la apropiación de recursos es todo Para bases de datos u otros recursos compartidos, y la implementación actual está distribuida, por lo que la función de sincronizados en el código habitual parece ser mucho menor.

Sin embargo, todos verán este campo a menudo. Por ejemplo, sabemos que ArrayList no es seguro para subprocesos. ¿Por qué Vector es seguro para subprocesos? Oh, resulta que se agrega un bloqueo sincronizado para evitar adiciones y eliminaciones simultáneas. HashMap es subproceso inseguro. Cuando ocurre una colisión concurrente y hash, es casi equivalente a operar una LinkedList, y puede haber problemas al agregarla. ¿Por qué ConcurrentHashMap es seguro para subprocesos? Oh, resultó ser a través de la operación CAS y el uso de bloqueo sincronizado ranuras para refinar el bloqueo granular, evitando los posibles problemas de HashMap.

Bien, echemos un vistazo a la palabra clave sincronizada.

Sincronizado generalmente significa bloquear métodos, métodos estáticos y bloques de código. En algunos lugares, se dice que bloqueará clases. De hecho, hay dos tipos en general, bloqueos a nivel de clase y bloqueos a nivel de método. Veamos el código:

1. Bloquear bloques de código de sincronización y bloquear objetos.

public class SyncThread implements Runnable {

    private Integer number = 0;

    public void method() throws Exception{
        synchronized (this) {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName()+"-synchronized锁同步代码块:"+number++);
                Thread.sleep(10);
            }
        }

    }

    @Override
    public void run() {
        try {
            method();
        } catch (Exception e) {

        }
    }
}

Vamos a ejecutarlo

    public static void main(String[] args) {

        SyncThread syncThread = new SyncThread();
         Thread thread1 = new Thread(syncThread, "SyncThread1");
         Thread thread2 = new Thread(syncThread, "SyncThread2");
         thread1.start();
         thread2.start();
    }

Creamos un objeto SyncThread, y luego creamos dos subprocesos a través de SyncThread, y luego comenzamos, lo que significa que ambos subprocesos operarán con el mismo número, por supuesto, el primero se bloqueará, y luego se completará la ejecución, el siguiente subproceso continúa ejecutar, entonces el resultado es:

SyncThread1-synchronized锁同步代码块:0
SyncThread1-synchronized锁同步代码块:1
SyncThread1-synchronized锁同步代码块:2
SyncThread1-synchronized锁同步代码块:3
SyncThread1-synchronized锁同步代码块:4
SyncThread2-synchronized锁同步代码块:5
SyncThread2-synchronized锁同步代码块:6
SyncThread2-synchronized锁同步代码块:7
SyncThread2-synchronized锁同步代码块:8
SyncThread2-synchronized锁同步代码块:9

Algunos socios se sorprenderán. ¿Por qué no subprocesan un subproceso dos ejecución y acumulación alternativas? Operan el mismo método para implementar ejecutable, que es equivalente a un bloqueo de subprocesos sincronizado después de ingresar al método. ¿De dónde proviene la ejecución alternativa?

Por supuesto, si desea actuar alternativamente, puede crear dos SyncThread.


         Thread thread1 = new Thread(new SyncThread(), "SyncThread1");
         Thread thread2 = new Thread(new SyncThread(), "SyncThread2");
         thread1.start();
         thread2.start();

Una vez corriendo, clang. .

SyncThread1-synchronized锁同步代码块:0
SyncThread2-synchronized锁同步代码块:0
SyncThread2-synchronized锁同步代码块:1
SyncThread1-synchronized锁同步代码块:1
SyncThread2-synchronized锁同步代码块:2
SyncThread1-synchronized锁同步代码块:2
SyncThread2-synchronized锁同步代码块:3
SyncThread1-synchronized锁同步代码块:3
SyncThread2-synchronized锁同步代码块:4
SyncThread1-synchronized锁同步代码块:4

Descubrí que Thread 1 y Thread 2 están haciendo sus propias cosas. ¿Son originalmente dos clases? Los bloqueos son métodos y las variables son variables ordinarias. No hay competencia entre los dos objetos. Tenga en cuenta que son objetos. Entonces, ¿cómo tienen una relación competitiva? Es decir, el bloqueo se actualiza a un bloqueo de clase. La forma más sencilla es agregar estático al número, lo que equivale a vincular al objeto. Todas las clases operan en este número. Por supuesto, en este momento, si seguimos haciendo bloques de código, definitivamente habrá preguntas concurrentes. El bloqueo debe elevarse a bloqueos de clase o bloqueos de objetos estáticos. Veamos el código:

 private static Integer number = 0;

    public void method() throws Exception{
        synchronized (number) {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName()+"-synchronized锁同步代码块:"+number++);
                Thread.sleep(10);
            }
        }

    }

Luego, ejecute el tipo de método de prueba para dos objetos:

SyncThread1-synchronized锁同步代码块:0
SyncThread2-synchronized锁同步代码块:1
SyncThread2-synchronized锁同步代码块:2
SyncThread1-synchronized锁同步代码块:3
SyncThread2-synchronized锁同步代码块:4
SyncThread1-synchronized锁同步代码块:5
SyncThread2-synchronized锁同步代码块:6
SyncThread2-synchronized锁同步代码块:7
SyncThread1-synchronized锁同步代码块:8
SyncThread1-synchronized锁同步代码块:9

Process finished with exit code 0

Si todavía está sincronizado (esto), habrá problemas de concurrencia, por lo que puede intentarlo usted mismo.

2. Método de bloqueo

    public synchronized void method() throws Exception {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "-synchronized锁同步代码块:" + number++);
            Thread.sleep(10);
        }
    }

Veamos los resultados de la operación:

SyncThread1-synchronized锁同步代码块:0
SyncThread2-synchronized锁同步代码块:1
SyncThread2-synchronized锁同步代码块:2
SyncThread1-synchronized锁同步代码块:2
SyncThread1-synchronized锁同步代码块:3
SyncThread2-synchronized锁同步代码块:4
SyncThread1-synchronized锁同步代码块:5
SyncThread2-synchronized锁同步代码块:6
SyncThread1-synchronized锁同步代码块:7
SyncThread2-synchronized锁同步代码块:8

Process finished with exit code 0

En este momento, puede ver que el método de bloqueo y el bloque de código de sincronización de bloqueo están en realidad al mismo nivel. Ambos bloquean el bucle en el método y toman conjuntamente el número de recurso.

¿Qué pasa con los métodos estáticos?

    public static synchronized void method() throws Exception {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "-synchronized锁同步代码块:" + number++);
            Thread.sleep(10);
        }
    }

Los métodos estáticos son obviamente bloqueos a nivel de clase. Puedes ver los resultados de la operación:

SyncThread1-synchronized锁同步代码块:0
SyncThread1-synchronized锁同步代码块:1
SyncThread1-synchronized锁同步代码块:2
SyncThread1-synchronized锁同步代码块:3
SyncThread1-synchronized锁同步代码块:4
SyncThread2-synchronized锁同步代码块:5
SyncThread2-synchronized锁同步代码块:6
SyncThread2-synchronized锁同步代码块:7
SyncThread2-synchronized锁同步代码块:8
SyncThread2-synchronized锁同步代码块:9

Process finished with exit code 0

Una vez que el subproceso ocupa el recurso, el subproceso esperará a que se complete este método antes de liberar el recurso, de hecho, es esta clase la que está bloqueada.

Por supuesto, esto también es un efecto.


    public synchronized void method() throws Exception {
        synchronized (SyncThread.class) {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + "-synchronized锁同步代码块:" + number++);
                Thread.sleep(10);
            }
        }
    }

Bueno, hemos entendido aproximadamente el bloqueo sincronizado, entonces, ¿cómo sincronizado se da cuenta del mecanismo de bloqueo?

Todos sabemos que los objetos se crean en el montón. Y el diseño de almacenamiento del objeto en la memoria se puede dividir en tres áreas: encabezado del objeto, datos de instancia y relleno de alineación. Entre ellos, el encabezado del objeto contiene información de bloqueo. Los siguientes son los datos en ejecución del propio encabezado del objeto.

Contenido de la tienda Bit de bandera estado
El código hash del objeto, la edad de generación del objeto. 01 No está bloqueado
Puntero al registro de bloqueo 00 Cerradura ligera
Puntero a bloqueo de peso pesado 10 Bloqueo de peso pesado
aire 11 Marca GC
Sesgo de ID de hilo, sesgo de marca de tiempo, edad de generación 01 Puede estar sesgado

 

Por supuesto, también hay un puntero de tipo, y la JVM usa este puntero para determinar qué instancia de clase es el objeto.

El bloqueo sincronizado es en realidad un bloqueo mixto de optimismo y pesimismo. El bloqueo optimista se procesa primero. Si falla, se utiliza el bloqueo pesimista. El bloqueo pesimista, es decir, el bloqueo pesado, se implementa a través del monitor. Bloqueo de objeto sincronizado, su puntero apunta a la dirección de un objeto monitor, cada objeto tendrá un monitor, donde el monitor se puede crear y destruir junto con el objeto, o se puede generar automáticamente cuando el hilo intente adquirir el bloqueo. El monitor está implementado por C ++. Aquellos que estén interesados ​​pueden explorarlo. No hablaré de eso aquí.

Compilamos y visualizamos el método de bloqueo del bloque de código de sincronización anterior (javap). Podemos ver que monitorenter y monitorexit en realidad están bloqueando y liberando el bloqueo. Al ejecutar la instrucción monitorenter, primero debemos intentar adquirir el objeto lock, es decir, el objeto monitor.Si el objeto no está bloqueado, o el hilo actual ya posee el bloqueo de este objeto, entonces aumentamos el valor del bloqueo. en 1 (así que sincronizado está bien Reentrant), por supuesto, el monitor se reducirá en 1 cuando se suelte.

De acuerdo, eso es todo sincronizado, hay algo mal ahí, puedes señalarlo en los comentarios, ¡es muy agradecido!

 

Sin sacrificio no hay victoria !

Supongo que te gusta

Origin blog.csdn.net/zsah2011/article/details/107946415
Recomendado
Clasificación