18.等待唤醒机制(wait、notify)
等待唤醒机制(wait、notify)
引言
本节介绍一个和同步锁都相关的知识点,等待唤醒机制。下面从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方法的作用是唤醒所有等待的线程,在实际开发中以上方法经常被使用。

本文来自博客园,作者:小陈子博客,转载请注明原文链接:https://www.cnblogs.com/cj8357475/p/16086046.html

浙公网安备 33010602011771号