等待和通知
等待和通知的标准范式
等待方:
1:获取对象的锁
2:在循环中判断条件是否满足,不满足调用wait方法继续阻塞,为啥要要循环中判断呢?因为该线程被唤醒之后可能条件依旧不满足
3:条件满足,执行业务逻辑
通知方:
1:获取对象的锁
2:改变相关条件
3:通知所有等待在对象的线程
都是属于Object的方法
等待:wait
通知:notify/notifyAll
需求:一个快递在变更;里程数和地点的时候通知等待的线程处理变更后的请求
测试使用notifyAll唤醒
实体类
package org.dance.day1.wn; /** * 快递实体类 * * @author ZYGisComputer */ public class Express { public final static String CITY = "ShangHai"; /** * 快递运输的里程数 */ private int km; /** * 快递到达的地点 */ private String site; public Express() { } public Express(int km, String site) { this.km = km; this.site = site; } /** * 变化公里数:然后通知处于wait状态并需要处理公里数的线程进行业务处理 */ public synchronized void checkKm() { // 变化公里数 this.km = 101; // 全部通知 notifyAll(); } /** * 变化地点:然后通知处于wait状态并需要处理地点的线程进行业务处理 */ public synchronized void checkSite() { // 变化城市 this.site = "BeiJin"; // 全部通知 notifyAll(); } public synchronized void waitKm() { // 循环等待 while (this.km <= 100) { try { wait(); System.out.println("check km " + Thread.currentThread().getId()); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("the km is " + this.km + ", I will change DB."); } public synchronized void waitSite() { // 循环等待 while (CITY.equals(this.site)) { try { wait(); System.out.println("check site " + Thread.currentThread().getId()); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("the site is " + this.site + ", I will change DB."); } }
测试类
package org.dance.day1.wn; import org.dance.tools.SleepTools; /** * 测试wait notify/notifyAll * @author ZYGisComputer */ public class TestWN { private static Express express = new Express(0,Express.CITY); /** * 检查里程数变化的线程,不满足一直等待 */ private static class CheckKm extends Thread{ @Override public void run() { express.waitKm(); } } /** * 检查城市变化的线程,不满足一直等待 */ private static class CheckSite extends Thread{ @Override public void run() { express.waitSite(); } } public static void main(String[] args) { for (int i = 0; i < 3; i++) { new CheckKm().start(); } for (int i = 0; i < 3; i++) { new CheckSite().start(); } SleepTools.second(1); // 修改里程数 express.checkKm(); } }
测试结果:
check site 16 check site 15 check site 14 check km 13 the km is 101, I will change DB. check km 12 the km is 101, I will change DB. check km 11 the km is 101, I will change DB.
测试发现全部的线程全部被唤醒了,然后其中三个等待城市变化的线程再次进入阻塞,另外三个等待里程数变化的执行成功退出阻塞
测试使用notify唤醒
返回结果:
check km 11
the km is 101, I will change DB.
check site 11
因为notify通知任意一个在这个对象上阻塞的线程,如果正好通知到了,等待里程数的,那么也只有一个被唤醒,其他两个继续阻塞,如果通知到了一个等待城市变化的那么这个线程将继续进入阻塞,所以看来,我们应该尽量使用notifyAll少用notify,因为可能发生信号丢失的情况
/** * Wakes up a single thread that is waiting on this object‘s * monitor. If any threads are waiting on this object, one of them * is chosen to be awakened. The choice is arbitrary and occurs at * the discretion of the implementation. A thread waits on an object‘s * monitor by calling one of the {@code wait} methods. * <p> * The awakened thread will not be able to proceed until the current * thread relinquishes the lock on this object. The awakened thread will * compete in the usual manner with any other threads that might be * actively competing to synchronize on this object; for example, the * awakened thread enjoys no reliable privilege or disadvantage in being * the next thread to lock this object. * <p> * This method should only be called by a thread that is the owner * of this object‘s monitor. A thread becomes the owner of the * object‘s monitor in one of three ways: * <ul> * <li>By executing a synchronized instance method of that object. * <li>By executing the body of a {@code synchronized} statement * that synchronizes on the object. * <li>For objects of type {@code Class,} by executing a * synchronized static method of that class. * </ul> * <p> * Only one thread at a time can own an object‘s monitor. * * @throws IllegalMonitorStateException if the current thread is not * the owner of this object‘s monitor. * @see java.lang.Object#notifyAll() * @see java.lang.Object#wait() */ public final native void notify();
在源码中可以看到,这个方法是一个 native的
在他的描述中有一段
The choice is arbitrary and occurs at,the discretion of the implementation.
翻译为中文
选择是任意的,发生在,执行的自由裁量权。
所以说notify的唤醒是随意的,并且信号只发出一次
但是据有人说,在线程进入等待的时候会进入一个等待队列,notify会唤醒第一个等待的线程
经过在百度上一顿搜索,浏览了大量的文章之后
我得到的结果就是在HotSpot虚拟机当中 notify唤醒的是阻塞线程队列当中的第一个ObjectWaiter节点,其他虚拟机不一定.
我觉得这个问题也已当做一个在面试的时候,你问面试官的一个技术性问题
作者:彼岸舞
时间:2020\09\16
内容关于:并发编程
本文来源于网络,只做技术分享,一概不负任何责任
原文:https://www.cnblogs.com/flower-dance/p/13678742.html