Explicación detallada de multithreaded_wait() y notificar()

1. Uso básico del mecanismo de notificación de espera

public class Test {
    
    
 
    public static void main(String[] args) throws InterruptedException {
    
    
        Thread waitThread = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                synchronized (Test.class){
    
    
                    System.out.println(Thread.currentThread().getName() + " is holding lock...");
                    try {
    
    
                        Thread.currentThread().sleep(1000);
                        System.out.println();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    try {
    
    
                        System.out.println("wait() will release lock");
                        Test.class.wait();
                        System.out.println(Thread.currentThread().getName() + " need hold the lock again, and then to 
                        continue...");
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " will release lock again");
                }
            }
        }, "waitThread");
 
        Thread notifyThread = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println(Thread.currentThread().getName() + " blocked.... for the lock");
                synchronized (Test.class){
    
    
                    System.out.println(Thread.currentThread().getName() + " is holding lock...");
                    Test.class.notify();
                    try {
    
    
                        Thread.currentThread().sleep(1000);
                        System.out.println();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " will release lock");
                }
            }
        }, "notifyThread");
 
        waitThread.start();
        Thread.sleep(100);
        notifyThread.start();
 
    }
 
}
 
 
输出结果:
waitThread is holding lock...
notifyThread blocked.... for the lock
 
wait() will release lock
notifyThread is holding lock...
 
notifyThread will release lock
waitThread need hold the lock again, and then to continue...
waitThread will release lock again
 
Process finished with exit code 0

2. Resumen

La premisa de las llamadas a los métodos wait() y notificar() es que el cuerpo del subproceso donde se encuentran los dos (es decir, el cuerpo del método run()) primero debe adquirir el bloqueo del objeto sincronizado; cuando el subproceso waitThread llama al método wait(), se liberará
El objeto de bloqueo, el subproceso cambia del estado EN EJECUCIÓN al estado EN ESPERA, es decir, el subproceso entra en la cola de espera;
antes de que el método wait() libere el objeto de bloqueo, la notificación Subproceso el subproceso se bloqueará en la posición en la que synchrozied adquiere el objeto de bloqueo;
y cuando wait() libere el bloqueo, notifique Subproceso El subproceso intentará competir por el bloqueo desde el estado bloqueado, obtener el bloqueo y comenzar a ejecutar el bloque de sincronización Cuando se ejecuta el método de notificación (), el subproceso waitThread se moverá de la cola de espera a la cola de sincronización, es decir, el método de notificación activará el inicio del subproceso waitThread. estado BLOQUEADO;
es decir, el método de notificación () solo activa el subproceso donde se encuentra la espera () para ingresar a la cola de sincronización competitiva de bloqueo desde la cola de espera no competitiva, y cambia del estado ESPERANDO al estado BLOQUEADO; si desea ejecutar wait() La premisa del código de seguimiento del método es que waitThread primero debe competir por el objeto de bloqueo, y dado que el bloque de sincronización donde se encuentra notificar () actualmente mantiene el bloqueo, el bloqueo es liberado desde el momento en que se acaba de llamar al método de notificación (activando waitThread para participar en la competencia) hasta el bloque de sincronización donde se encuentra (Liberar lo que necesita waitThread) En este proceso, waitThread solo puede estar bloqueado todo el tiempo.

inserte la descripción de la imagen aquí

Esperando el mecanismo de notificación:
el subproceso en espera adquiere el bloqueo del objeto, llama al método wait (), abandona el bloqueo e ingresa a la cola de espera. El
subproceso de notificación adquiere el bloqueo del objeto, llama al método de notificación () del objeto
para esperar el subproceso para recibir la notificación, y pasa de la cola de espera a la cola de espera. Sincronice la cola e ingrese al estado de bloqueo.
Después de que se notifique al subproceso que libere el bloqueo, espere a que el subproceso adquiera el bloqueo y continúe con la ejecución.

A partir de la descripción anterior, se puede ver que, ya sea que applyThread compita por el bloqueo o después de la notificación, waitThread debe volver a ingresar a la cola de sincronización, volver a competir por el bloqueo y, finalmente, debe competir por el mismo objeto de bloqueo en el bloque. Por lo tanto, el mecanismo de espera/notificación sigue confiando en el mecanismo de sincronización.

3. Usa detalles comunes:

Hagamos un experimento, intente ejecutar un bucle while después de que se llame al método de notificación, para no permitir que se ejecute el bloque de sincronización. En este caso, aunque wait() ha recibido un recordatorio de espera para bloquear, todavía no puede lock y no puede Continuar para ejecutar el código subsiguiente:

public class Test {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Thread waitThread = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                synchronized (Test.class){
    
    
                    try {
    
    
                        System.out.println(Thread.currentThread().getName() + " hold lock");
                        Test.class.wait();
                        System.out.println(Thread.currentThread().getName() + " hold lock again!!!");
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        }, "waitThread");
 
        Thread notifyThread = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                synchronized (Test.class){
    
    
                    System.out.println(Thread.currentThread().getName() + " hold lock");
                    Test.class.notify();
                    while (true);
                }
            }
        }, "notifyThread");
 
        waitThread.start();
        Thread.sleep(100);
        notifyThread.start();
 
    }
 
}
 
输出结果:
waitThread hold lock
notifyThread hold lock
 

Se puede ver a partir de los resultados de salida que, aunque notificar () notifica el método de espera (), pero debido a que hay un bucle infinito en el bloque de sincronización donde se encuentra la notificación, es decir, el bloqueo requerido por esperar () no se ha liberado , por lo que el subproceso waitThread siempre estará bloqueado;

4. Proceso de cambio de estado del subproceso

A continuación, obtenemos el estado del hilo a través del método getState para comprender de forma más intuitiva el proceso de espera y notificación de los mecanismos que cambian el estado del hilo:

import org.apache.poi.ss.formula.functions.T;
 
public class Test {
    
    
 
    private static Thread waitThread;
    public static void main(String[] args) throws InterruptedException {
    
    
        waitThread = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                synchronized (Test.class){
    
    
                    try {
    
    
                        System.out.println(Thread.currentThread().getName() + " hold lock");
                        Test.class.wait();
                        System.out.println(Thread.currentThread().getName() + " hold lock again!!!");
                        for (int i=0; i<5;i++){
    
    
                            try {
    
    
                                Thread.currentThread().sleep(1000);
                                System.out.println(Thread.currentThread().getName() + "'s state after hold lock again: "
                                        + Thread.currentThread().getState());
                            } catch (InterruptedException e) {
    
    
                                e.printStackTrace();
                            }
                        }
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        }, "waitThread");
 
        Thread notifyThread = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                synchronized (Test.class){
    
    
                    System.out.println(Thread.currentThread().getName() + " hold lock");
                    for (int i=0; i<5;i++){
    
    
                        try {
    
    
                            Thread.currentThread().sleep(1000);
                            System.out.println(waitThread.getName() + "'s state before notify: " + 
                            waitThread.getState());
                        } catch (InterruptedException e) {
    
    
                            e.printStackTrace();
                        }
 
                    }
                    System.out.println("----------------------------------------");
                    Test.class.notify();
                    for (int i=0; i<5;i++){
    
    
                        try {
    
    
                            Thread.currentThread().sleep(1000);
                            System.out.println(waitThread.getName() + "'s state after notify: " + 
                            waitThread.getState());
                        } catch (InterruptedException e) {
    
    
                            e.printStackTrace();
                        }
                    }
                    System.out.println("----------------------------------------");
                }
            }
        }, "notifyThread");
 
        waitThread.start();
        Thread.sleep(100);
        notifyThread.start();
 
    }
 
}

输出结果:
waitThread hold lock
notifyThread hold lock
waitThread's state before notify: WAITING
waitThread's state before notify: WAITING
waitThread's state before notify: WAITING
waitThread's state before notify: WAITING
waitThread's state before notify: WAITING
----------------------------------------
waitThread's state after notify: BLOCKED
waitThread's state after notify: BLOCKED
waitThread's state after notify: BLOCKED
waitThread's state after notify: BLOCKED
waitThread's state after notify: BLOCKED
----------------------------------------
waitThread hold lock again!!!
waitThread's state after hold lock again: RUNNABLE
waitThread's state after hold lock again: RUNNABLE
waitThread's state after hold lock again: RUNNABLE
waitThread's state after hold lock again: RUNNABLE
waitThread's state after hold lock again: RUNNABLE
Process finished with exit code 0

5. El paradigma clásico de esperar/avisar

El paradigma se divide en dos partes, una para el lado que espera (consumidor) y otra para el lado que notifica (productor).
El grupo de espera sigue los siguientes principios.
Adquiere un bloqueo en un objeto.
Si no se cumple la condición, llame al método wait() del objeto y aún verifique la condición después de recibir la notificación.
Si se cumple la condición, se ejecuta la lógica correspondiente.

// 对应的伪代码如下:
synchronized(对象) {
    
    
    while(条件不满足) {
    
    
        对象.wait();
    }
    对应的处理逻辑
}  
 
理解说明:
while在这里是有意义的,当notify的同步块释锁之后,
wait()竞争得到锁,进入第二轮循环,若此时条件仍不满足,
那么wait()被第二次执行,由于这次执行之前,当前线程仍持有锁,
所以wait有锁可释,不会报错,释完锁之后,当前线程就由RUNNING转向WAITING,
线程进入等待队列,等待下一次notify通知.

El notificante sigue el siguiente principio:
obtener el candado del objeto.
Cambiar condiciones.
Notifica a todos los subprocesos que esperan en el objeto.

// 对应的伪代码如下:
synchronized(对象) {
    
    
    // 改变条件得位置不重要 ,只要在同步块执行之前(释锁之前),
    // 因为wait只有在notify释锁后才有机会加锁,进而响应条件的变化
    改变条件
    对象.notifyAll();
}

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/chuige2013/article/details/129965856
Recomendado
Clasificación