18.等待唤醒机制(wait、notify)

 

等待唤醒机制(waitnotify

 引言

本节介绍一个和同步锁都相关的知识点,等待唤醒机制。下面从3点来介绍等待唤醒机制。

第一点是当前线程等待,

第二点唤醒单个等待的线程,

第三点唤醒所有等待的线程。

 使当前线程等待

首先来看第一点,使当前线程等待,使用wait方法即可让当前线程等待,

该方法只能被锁对象调用,而锁有对象都可以成为锁。所以该方法就位于OBD的类里面,锁有对象都可以调用该方法。调用该方法被中断,则引发线程中断异常。

下面来演示该方法。自定义一个任务,任务内容是使当前线程等待,等待之后随便输出一句话,但我们知道调用wait方法需要锁,而锁必然从同步中获取。


所以这里我们先定义一个同步代码块(wait必须和
synchronized一起用,不然会直接报错,同步锁为this,这里的this指代的就是task、对象,现在锁我们已经拿到了,下面调用锁对象的wait方法使当前线程等待。不过wait的方法有抛异常,使用try-catch捕获,之后随便输出一句话

task

package com.chenjie.executor.day18;


import com.chenjie.executor.day16.Main;

/**
 * packageName com.chenjie.executor.day14
 *
 * @author chenjie
 * @version JDK 8
 * @className TicketTask (此处以class为例)
 * @date 2024/5/28
 * @description TODO
 */
public class Task implements Runnable {


    @Override
    public void run() {
            synchronized (this) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("人人都是程序员");
            }
        }


    }

main

下面我们来执行任务,首先将任务创建出来,然后创建一个线程,接着将任务传递给线程,最后启动线程。

package com.chenjie.executor.day18;



/**
 * packageName com.chenjie.executor.day09
 *
 * @author chenjie
 * @version JDK 8
 * @className Main1 (此处以class为例)
 * @date 2024/5/27
 * @description TODO
 */
public class Main {

    /**
     * 创建三个线程来跑这段代码
     * @param args
     */
    public static void main(String[] args) {
        Task task=new Task();
        Thread thread = new Thread(task,"thread");
        thread.start();


    }
}

结果

从执行结果来看,线程的确被等待,程序未结束,

 唤醒单个等待的线程

第一点介绍完了。再来看第二点,唤醒单个等待的线程,使用notify方法就可以唤醒单个等待的线程。 

 

notify


该方法同样需要被锁对象调用,也是位于
object类里面,下面使用notify方法,唤醒刚刚等待的线程,接着之前的程序写,为了唤醒效果更明显,使主线程休眠一秒钟,一秒钟之后才唤醒等待中的线程。刚刚的锁的对象是task对象,所以这里使用task对象调用notify方法,唤醒在该锁上等待的单个线程。不要忘了notify方法需要当前线程拥有对应的锁才可以被调用。所以调用notify方法的语句需要写在同步代码块里面,

package com.chenjie.executor.day18;


import lombok.Synchronized;

/**
 * packageName com.chenjie.executor.day09
 *
 * @author chenjie
 * @version JDK 8
 * @className Main1 (此处以class为例)
 * @date 2024/5/27
 * @description TODO
 */
public class Main {

    /**
     *
     * @param args
     */
    public static void main(String[] args) {
        Task task=new Task();
        Thread thread = new Thread(task,"thread");
        thread.start();
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        synchronized(task){
            task.notify();
        }
    }
}

 结果

下面来看看执行结果。从执行结果来看,一秒钟后等待的线程被唤醒,程序结束

证明notify方法只能唤醒单个等待的线程

为证明notify方法只能唤醒单个等待的线程。 

我们再创建两个线程,一共是三个线程在等待。

 

package com.chenjie.executor.day18;


import lombok.Synchronized;

/**
 * packageName com.chenjie.executor.day09
 *
 * @author chenjie
 * @version JDK 8
 * @className Main1 (此处以class为例)
 * @date 2024/5/27
 * @description TODO
 */
public class Main {

    /**
     *
     * @param args
     */
    public static void main(String[] args) {
        Task task=new Task();
        Thread thread = new Thread(task,"thread");
        Thread thread1 = new Thread(task,"thread1");
        Thread thread2 = new Thread(task,"thread2");
        thread.start();
        thread1.start();
        thread2.start();
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        synchronized(task){
            task.notify();
        }
    }
}

结果

下面我们再来看看执行结果,从执行结果来看,只有一个线程被唤醒,只输出了一句话,还有两个线程依旧在等待,程序因还有线程未执行完而无法结束。

 唤醒所有等待的线程

下面我们再来介绍如何唤醒所有等待的线程,使用notifyall方法就可以唤醒所有等待的线程,该方法同样需要被锁对象调用,也是位于object类里面。

下面使用notifyall方法唤醒刚刚所有等待的线程,改写之前的程序,将notify方法换成notifyall方法即可。 

package com.chenjie.executor.day18;


/**
 * packageName com.chenjie.executor.day09
 *
 * @author chenjie
 * @version JDK 8
 * @className Main1 (此处以class为例)
 * @date 2024/5/27
 * @description TODO
 */
public class Main1 {

    /**
     *
     * @param args
     */
    public static void main(String[] args) {
        Task task=new Task();
        Thread thread = new Thread(task,"thread");
        Thread thread1 = new Thread(task,"thread1");
        Thread thread2 = new Thread(task,"thread2");
        thread.start();
        thread1.start();
        thread2.start();
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        synchronized(task){
            task.notifyAll();
        }
    }
}

结果

下面再来看看执行结果,从执行结果来看,所有线程被唤醒。还输出了三句话程序也结束了

 

 总结

最后来总结一下本节的内容。本节介绍了与同步同步锁相关的等待唤醒机制,这里列举了5个方法,其中前3个方法和等待相关,后2个方法和唤醒相关,wait方法的作用是使当前线程等待,它还有两个重载方法可以指定等待超时时间,若等待超时则自动唤醒。notify方法的作用,是唤醒单个等待的线程,notifyall方法的作用是唤醒所有等待的线程,在实际开发中以上方法经常被使用。 

 

posted @ 2022-05-03 19:24  小陈子博客  阅读(1036)  评论(0)    收藏  举报