Comprensión de las cerraduras reentrantes en JAVA

Gracias a "StoneWang" por un resumen profundo del alma de los bloqueos reentrantes: Reentrant significa que un hilo ya ha adquirido un bloqueo determinado y puede adquirir este bloqueo de nuevo sin interbloqueo.

1. Bloqueo reentrante

  • sincronizado
  • ReentrantLock

Dos características de las cerraduras reentrantes:

  • Cuando un hilo A adquiere el bloqueo, entonces el hilo A puede adquirir el bloqueo nuevamente sin liberar el bloqueo actual
  • Otros hilos no pueden obtener este bloqueo. Solo después de que este subproceso A libera el bloqueo actual, otros subprocesos pueden adquirir este bloqueo.

1.1 Las características de las cerraduras reentrantes sincronizadas:

public class SynchronizedLockTest {
    
    
    //类锁,实际是锁类的class对象
    private static synchronized void synClass(){
    
    

        Thread thread = Thread.currentThread();
        System.out.println("当前执行synClass的线程名称:" + thread.getName());

        System.out.println("synClass going...");
            subSynClass();
        System.out.println("synClass end");
    }



    private static synchronized void subSynClass(){
    
    
        Thread thread = Thread.currentThread();
        System.out.println("当前执行 subSynClass 的线程名称:" + thread.getName());

        System.out.println("subSynClass going...");
        SleepTools.second(3);
        System.out.println("subSynClass end");
    }

    public static void main(String[] args) {
    
    
        Thread thread = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println("第一个线程开始执行");
                synClass();
            }
        });

        Thread thread2 = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println("第二个线程开始执行");
                synClass();
            }
        });

        thread.setName("第一个线程");
        thread2.setName("第二个线程");

        thread.start();
        thread2.start();


    }
}

Resultado de salida:

第一个线程开始执行
第二个线程开始执行
当前执行synClass的线程名称:第一个线程
synClass going...
当前执行 subSynClass 的线程名称:第一个线程
subSynClass going...
subSynClass end
synClass end
当前执行synClass的线程名称:第二个线程
synClass going...
当前执行 subSynClass 的线程名称:第二个线程
subSynClass going...
subSynClass end
synClass end

Puede ver que en este ejemplo se usa el bloqueo de clase sincronizado, lo que significa que solo hay un bloqueo en toda esta clase.

  • La primera característica de la reentrada sincronizada se muestra en: Cuando el "primer hilo" ejecuta el método SynClass () sincronizado y obtiene el bloqueo, aún puede llamar a la subSynClass sincronizada en SynClass () ()método.
  • La segunda característica de la reentrada sincronizada: cuando el "segundo hilo" llama a synClass. Debido a que el "primer hilo" no ha liberado el bloqueo, el "segundo hilo" se bloqueará y esperará.

Nota: sincronizado liberará el bloqueo cuando se ejecute el método o bloque de código

1.2 Características de la cerradura reentrante ReentrantLock:

public class ReentrantLockTest2 {
    
    
    static Lock lock = new ReentrantLock();


    public static void methodTest() {
    
    
        System.out.println(Thread.currentThread().getName() + "进入方法等待.....");
        try {
    
    
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "methodTest do something start。。。。");
             subMethodTest();
            System.out.println(Thread.currentThread().getName() + "methodTest do something end。。。。。");

        } finally {
    
    
            lock.unlock();
        }

    }


    public static void subMethodTest() {
    
    
        try {
    
    
            lock.lock();

            System.out.println(Thread.currentThread().getName() + "subMethodTest do something start");
            SleepTools.second(1);
            System.out.println(Thread.currentThread().getName() + "subMethodTest do something end");

        } finally {
    
    
            lock.unlock();
        }

    }


    public static void main(String[] args) {
    
    
        Thread thread = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                methodTest();
            }
        });

        Thread thread2 = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                methodTest();
            }
        });

        thread.setName("A线程");
        thread2.setName("B线程");

        thread.start();
        thread2.start();

    }
}

Resultado de salida

A线程进入方法等待.....
B线程进入方法等待.....
A线程methodTest do something start。。。。
A线程subMethodTest do something start
A线程subMethodTest do something end
A线程methodTest do something end。。。。。
B线程methodTest do something start。。。。
B线程subMethodTest do something start
B线程subMethodTest do something end
B线程methodTest do something end。。。。。
  • La primera característica de la reentrada de ReentrantLock se muestra en: Un hilo ejecuta methodTest () para obtener el bloqueo de bloqueo, y cuando se llama a subMethodTest (), aún puede ingresar el segmento de código bloqueado por el bloqueo.
  • La segunda característica de la reentrada sincronizada: cuando el hilo B llama a methodTest (), el bloque de código bloqueado por el candado no puede entrar y solo espera afuera.

La diferencia entre ReentrantLock y sincronizado es que debe liberar manualmente el bloqueo, por lo que debe liberar manualmente el bloqueo cuando utilice ReentrantLock, y el número de bloqueos debe ser el mismo que el número de liberaciones.

  • Si el "lock.unlock ();" en el método methodTest () del código anterior está comentado, el resultado de la impresión es el siguiente:
B线程进入方法等待.....
A线程进入方法等待.....
B线程methodTest do something start。。。。
B线程subMethodTest do something start
B线程subMethodTest do something end
B线程methodTest do something end。。。。。

El hilo B solo libera el bloqueo una vez, y el hilo A se bloqueará para siempre y no tendrá la oportunidad de ejecutarse.

1.3 Implementación de bloqueo reentrante

public class Lock{
    
    
    boolean isLocked = false;
    Thread  lockedBy = null;
    int lockedCount = 0;
    public synchronized void lock()
            throws InterruptedException{
    
    
        Thread thread = Thread.currentThread();
        while(isLocked && lockedBy != thread){
    
    
            wait();
        }
        isLocked = true;
        lockedCount++;
        lockedBy = thread;
    }
    public synchronized void unlock(){
    
    
        if(Thread.currentThread() == this.lockedBy){
    
    
            lockedCount--;
            if(lockedCount == 0){
    
    
                isLocked = false;
                notify();
            }
        }
    }
}

Lógica al bloquear:

  1. Al bloquear, obtenga primero el hilo actual. (Identifique quién necesita el candado)
 Thread thread = Thread.currentThread();
  1. Juicio: cuando el recurso crítico ha sido bloqueado, pero el hilo que actualmente solicita el bloqueo no es el hilo que bloqueó previamente el recurso crítico. Entonces, el hilo que actualmente solicita el bloqueo debe esperar.
while(isLocked && lockedBy != thread){
    
    
        wait();
}

Dos situaciones en las que se puede obtener el candado sin esperar:

  • Los subprocesos no utilizan el bloqueo actual.
  • El bloqueo actual es utilizado por un hilo, y el hilo que solicita el bloqueo actualmente es el hilo que usa el bloqueo.
  1. Después de conseguir el candado
isLocked = true; // 当前锁已经被使用了
lockedCount++;//锁的次数+1
lockedBy = thread;//记录拿到锁的线程

El hilo que solicita actualmente el bloqueo primero agrega el bloqueo, y luego el número de bloqueos + 1, y luego se asigna a sí mismo (este hilo) a lockBy para mostrar quién usa actualmente el bloqueo para facilitar el juicio while al volver a ingresar.

Lógica de desbloqueo:

  1. Primero, vea si el hilo que requiere desbloqueo es el hilo que actualmente usa el bloqueo. Sin hacer nada. (Por supuesto, no puede permitir que otros subprocesos ejecuten el código de desbloqueo para desbloquearlo y usarlo a voluntad. Eso es equivalente a que todos tengan una llave. El juicio aquí significa que el desbloqueo debe estar bloqueado)
  2. Luego, reduzca el número de bloqueos en uno.
  3. Luego se juzga si el número de bloqueos se ha convertido en 0.
  4. Cambiar a 0 significa que la cerradura se ha desbloqueado por completo. La bandera bloqueada está bloqueada se puede restablecer.
    Y despierta aleatoriamente un hilo esperando esperando (): notificar ()

2. Bloqueo no reentrante

Dado que el mismo subproceso puede ingresar al mismo bloqueo varias veces, se denomina bloqueo reentrante, un bloqueo no reentrante significa que el mismo subproceso no puede ingresar al mismo bloqueo varias veces.

public class NotReentrantLock{
    
    
    private boolean isLocked = false;

    public synchronized void lock() {
    
    
        while(isLocked){
    
    
            try {
    
    
                wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
        isLocked = true;
    }
    public synchronized void unlock(){
    
    
        isLocked = false;
        notify();
    }




    public static void main(String[] args) {
    
    
        NotReentrantLock lock = new NotReentrantLock();

        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    lock.lock();
                    System.out.println("第1次获取锁,这个锁是:" + lock);

                    int index = 1;
                    while (true) {
    
    
                        try {
    
    
                            lock.lock();
                            System.out.println("第" + (++index) + "次获取锁,这个锁是:" + lock);

                            try {
    
    
                                Thread.sleep(new Random().nextInt(200));
                            } catch (InterruptedException e) {
    
    
                                e.printStackTrace();
                            }

                            if (index == 10) {
    
    
                                break;
                            }
                        } finally {
    
    
                            lock.unlock();
                        }

                    }

                } finally {
    
    
                    lock.unlock();
                }
            }
        }).start();
    }
}

Salida:

1次获取锁,这个锁是:com.xiangxue.可重入锁.NotReentrantLock@15c02541

Puede ver que el mismo hilo está bloqueado cuando se adquiere el bloqueo por segunda vez

Referencia:
https://blog.csdn.net/w8y56f/article/details/89554060
https://blog.csdn.net/qq_29519041/article/details/86583945

Supongo que te gusta

Origin blog.csdn.net/langwuzhe/article/details/108556899
Recomendado
Clasificación