Java 多线程 —— wait 与 notify
引言
认识一下 Object 类中的两个和多线程有关的方法:wait 和 notify。
wait,当前线程进入 WAITING 状态,释放锁资源。
notify,唤醒等待中的线程,不释放锁资源。
一、使用 wait-notify 实现一个监控程序
实现一个容器报警功能,两个线程分别执行以下任务:
t1 为容器添加10个元素;
t2实时监控容器中元素的个数,当个数为5时,线程2给出提示并结束。
1.1 简单的 while-true 版本
/*** 一个普通的容器*/
class Container {private volatile List<Object> values = new ArrayList<>();public void add(Object value) {values.add(value);}public Integer sise() {return values.size();}
}
public class T_Alert01 {public static void main(String[] args) {Container container = new Container();new Thread(() -> {while (true) {if (container.sise() == 5) {System.out.println("alert 5!");break;}}}, "T2").start();new Thread(() -> {for (int i = 0; i < 10; i++) {container.add(new Object());System.out.println("已添加:" + container.sise());}}, "T1").start();}
}
程序解析:监控线程 T2 通过 while-true 监控容器内的元素数量,但当 size == 5 时,还未来得及报警,T1 就又添加了多个元素;而且 while-true 会浪费很多CPU 资源。
显然,这么做无法满足我们的要求。
1.2 wait-notify 初版
public class T_Alert02 {public static void main(String[] args) throws InterruptedException {Container container = new Container();final Object lock = new Object();new Thread(() -> {synchronized (lock) {if (container.sise() != 5) {try {lock.wait();} catch (InterruptedException e) {}}System.out.println("alert 5!");}}, "T2").start();TimeUnit.SECONDS.sleep(1);new Thread(() -> {synchronized (lock) {for (int i = 0; i < 10; i++) {container.add(new Object());System.out.println("已添加:" + container.sise());if (container.sise() == 5) {lock.notify();}}}}, "T1").start();}
}
程序解析:T2监控线程先去判断容器大小,如果未达到报警标准,则等待在 lock 对象上,WAITING 是一种挂起状态,不消耗CPU资源。
T1 线程在达到 5 个时,触发一个 notify方法,唤醒其他等待中的线程。然而,仅仅是 notify 还不足以做到实时唤醒 T2 报警,上述代码无论执行多少次都是最后输出报警信息,想想这是为什么?
1.3 wait-notify 完整版
public class T_Alert03 {public static void main(String[] args) throws InterruptedException {Container container = new Container();final Object lock = new Object();new Thread(() -> {synchronized (lock) {if (container.sise() != 5) {try {lock.wait();} catch (InterruptedException e) {}}System.out.println("alert 5!");lock.notify();}}, "T2").start();TimeUnit.SECONDS.sleep(1);new Thread(() -> {synchronized (lock) {for (int i = 0; i < 10; i++) {container.add(new Object());System.out.println("已添加:" + container.sise());if (container.sise() == 5) {lock.notify();try {lock.wait();} catch (InterruptedException e) {}}}}}, "T1").start();}
}
程序解析:该版本的 wait-notify 可以满足实际题目要求,达到实时触发警报,在 T1 notify 之后立刻调用 wait 进入状态;另一边T2在被唤醒并输出报警信息后,也需要再次调用 notify 唤醒 其他等待线程继续执行任务。
二、notify 和 notifyAll
如果有多个线程等待同一个对象锁,那么 notify 方法会随机唤醒一个线程,它无法做到精准唤醒。notifyAll 是唤醒全部等待线程,但需要明确是是,由于wait-notify 的操作模式是基于锁对象的,所以即便是 notifyAll 也是非公平竞争锁资源,即哪个线程抢到锁就去执行同步代码。
三、wait-notify 的原理
我们声明了一个 Object 对象,直接调用 wait 方法会怎样呢?
public class T_Wait_Notify {public static void main(String[] args) throws InterruptedException {final Object lock = new Object();lock.wait();}
}
监视器状态异常。这是因为 wait-notify 必须基于“锁对象”,而这个锁对象可不是普通的一个什么对象都可以。在了解了 synchronized 关键字的实现原理后,我们知道,JVM 为每个对象都关联了一个 monitor 对象,进入同步代码块和结束同步代码块就对应着 monitor enter 和 monitor exit 两条指令,也就是说,如果不使用 synchronized,就不存在所谓的 wait 和 notify。所以正确的写法一定是:
synchronized (lock) {lock.wait();
}
在 wait 方法的 Java doc 中这样说明,wait 方法会令当前线程将自己放入锁对象的 wait set 中,并且放弃此对象上所有同步的同步声明。
当唤醒时,当前线程必须持有该对象的监视器,才能继续执行。
总之,wait 和 notify 是和 对象的 monitor 紧密相关的,而 monitor 又是 synchronized 重量级锁模式的实现原理,所以理解wait 和 notify的 同时 也需要深入理解 synchronized 关键字。
扩展:闭锁实现的监控报警
闭锁是一种同步工具类,可以延迟线程的进度直到其到达终止状态。
闭锁相当于一扇门:在闭锁到达结束状态之前,这扇门一直是关闭的,不允许任何线程通过,当到达结束状态时,这扇门会打开并允许所有的线程通过。
下面的程序是通过 CountDownLatch 闭锁来实现的一个监控容器的版本,虽然可以达到要求,但很遗憾,程序中必须通过 sleep 方法让线程跑的“没那么快”,否则,去掉 sleep 的话依然会出现 while-true 的执行结果,即警报没那么实时了:
public class T_Alert04CountDownLatch {public static void main(String[] args) {Container container = new Container();CountDownLatch latch = new CountDownLatch(1);new Thread(() -> {if (container.sise() != 5) {try {latch.await();} catch (InterruptedException e) {}}System.out.println("alert 5!");}, "T2").start();new Thread(() -> {for (int i = 0; i < 10; i++) {container.add(new Object());System.out.println("已添加" + container.sise());if (container.sise() == 5) {latch.countDown();}try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {}}}, "T1").start();}
}
总结
使用 wait-notify 切换线程状态是一种细粒度操作,开发者需要非常了解他们的执行逻辑以及线程的生命周期。
wait 和 notify使用时必须将对象锁定,否则无法使用。
线程在使用对象的wait方法后会进入等待状态,notify() 和 notifyAll() 可以唤醒其他线程,注意 notify 是随机唤醒一个线程。
wait-notify的操作是相对复杂的,虽然强大,但是在处理复杂的业务逻辑中书写较麻烦,相当于多线程中的汇编语言。
使用CountDownLatch可以有效的替代wait和notify的使用场景,而且不受锁的限制,书写简便。
Java 多线程 —— wait 与 notify相关推荐
- Java多线程wait()和notify()方法图解
多线程wait()和notify()方法详解 文章目录 多线程wait()和notify()方法详解 前言 一.线程间等待与唤醒机制 二.等待方法wait() 三.唤醒方法notify() 四.关于w ...
- Java多线程中wait, notify and notifyAll的使用
本文为翻译文章,原文地址:http://www.journaldev.com/1037/java-thread-wait-notify-and-notifyall-example 在Java的Obje ...
- Java 多线程编程之 notify notifyAll wait lock unlock 算法
写了一个类来理解java 同步机制的算法.这个类并不适合实战,而仅仅是算法层面进行理解. package multithread; import java.util.ArrayList;import ...
- java多线程 wait和notify方法
public class ProductTest {public static void main(String[] args) {Clerk clerk = new Clerk();Producte ...
- Java多线程中notifyAll()方法使用教程
简介 本文将承接<Java多线程wait()和notify()系列方法使用教程>,结合代码实例,补充讲解下notifyAll()方法的作用以及使用时需要注意的地方. 一.notifyAll ...
- java 多线程 notifyall_java多线程之 wait(),notify(),notifyAll()
这几天在写一个java多线程服务器的功能,用到这些基础,自叹基础知识还需巩固,先写上一下这些说明,供自己和大家参考 wait(),notify(),notifyAll()不属于Thread类,而是属于 ...
- Java多线程之线程间协作 notify与wait的使用
(转载请注明出处:http://blog.csdn.net/buptgshengod) 1.背景 Java多线程操作运用很广,特别是在android程序方面.线程异步协作是多线程操作的难点也是关键,也 ...
- 【收藏】Java多线程/并发编程大合集
(一).[Java并发编程]并发编程大合集-兰亭风雨 [Java并发编程]实现多线程的两种方法 [Java并发编程]线程的中断 [Java并发编程]正确挂起.恢复.终止线程 [ ...
- 40个Java多线程问题总结
(转) 这篇文章作者写的真是不错 40个问题汇总 1.多线程有什么用? 一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡.所谓"知其然知其所 ...
最新文章
- linux文件属性文文件类型知识
- UA SIE545 优化理论基础4 对偶理论简介2 弱对偶与Duality Gap
- SDL及扩展库在ARM-Linux 完整移植
- mysql5.6 忘记root密码后,如何找回密码?
- LWIP的数据包管理
- 【数码管识别】4识别成5或7的问题
- Matter-JS friction 摩擦力
- Python爬虫:通过Selenium库学习如何爬取京东畅销排行榜书籍
- 批量下载哔哩哔哩视频的工具
- 苹果iPhone手机用iTunes更新IOS14.3系统失败怎么解决
- 计算机桌面底边出现库如何去掉,桌面图标有蓝底怎么去掉完美全解决方案
- vb6.0企业版win7_教你安装纯净版windows系统
- matplotlib之pyplot模块之饼图(pie():基础参数,返回值)
- Latex——属于符号
- CodeForces #379(734A|734B|734C|734D|734E|734F)|二分查找|模拟|树的半径|位运算
- 京东财报图解:年营收9516亿增28% 全渠道取得阶段性进展
- 线性代数 --- 线性相关与线性无关(个人学习笔记)
- moviepy剪切视频spleeter视频降噪-CPUGPU
- 《会计学》账户与复式记账笔记的思维导图
- 用 Python 写一个天天酷跑 | 内附源码