【Java并发编程】之十一:线程间通信中notify通知的遗漏
notify通知的遗漏很容易理解,即threadA还没开始wait的时候,threadB已经notify了,这样,threadB通知是没有任何响应的,当threadB退出synchronized代码块后,threadA再开始wait,便会一直阻塞等待,直到被别的线程打断。
遗漏通知的代码
下面给出一段代码演示通知是如何遗漏的,如下:
public class MissedNotify extends Object { private Object proceedLock; public MissedNotify() { print("in MissedNotify()"); proceedLock = new Object(); } public void waitToProceed() throws InterruptedException { print("in waitToProceed() - entered"); synchronized ( proceedLock ) { print("in waitToProceed() - about to wait()"); proceedLock.wait(); print("in waitToProceed() - back from wait()"); } print("in waitToProceed() - leaving"); } public void proceed() { print("in proceed() - entered"); synchronized ( proceedLock ) { print("in proceed() - about to notifyAll()"); proceedLock.notifyAll(); print("in proceed() - back from notifyAll()"); } print("in proceed() - leaving"); } private static void print(String msg) { String name = Thread.currentThread().getName(); System.out.println(name + ": " + msg); } public static void main(String[] args) { final MissedNotify mn = new MissedNotify(); Runnable runA = new Runnable() { public void run() { try { //休眠1000ms,大于runB中的500ms, //是为了后调用waitToProceed,从而先notifyAll,后wait, //从而造成通知的遗漏 Thread.sleep(1000); mn.waitToProceed(); } catch ( InterruptedException x ) { x.printStackTrace(); } } }; Thread threadA = new Thread(runA, "threadA"); threadA.start(); Runnable runB = new Runnable() { public void run() { try { //休眠500ms,小于runA中的1000ms, //是为了先调用proceed,从而先notifyAll,后wait, //从而造成通知的遗漏 Thread.sleep(500); mn.proceed(); } catch ( InterruptedException x ) { x.printStackTrace(); } } }; Thread threadB = new Thread(runB, "threadB"); threadB.start(); try { Thread.sleep(10000); } catch ( InterruptedException x ) {} //试图打断wait阻塞 print("about to invoke interrupt() on threadA"); threadA.interrupt(); }
}
执行结果如下:
分析:由于threadB在执行mn.proceed()之前只休眠了500ms,而threadA在执行mn.waitToProceed()之前休眠了1000ms,因此,threadB会先苏醒,继而执行mn.proceed(),获取到proceedLock的对象锁,继而执行其中的notifyAll(),当退出proceed()方法中的synchronized代码块时,threadA才有机会获取proceedLock的对象锁,继而执行其中的wait()方法,但此时notifyAll()方法已经执行完毕,threadA便漏掉了threadB的通知,便会阻塞下去。后面主线程休眠10秒后,尝试中断threadA线程,使其抛出InterruptedException。
修正后的代码
为了修正MissedNotify,需要添加一个boolean指示变量,该变量只能在同步代码块内部访问和修改。修改后的代码如下:
public class MissedNotifyFix extends Object { private Object proceedLock; //该标志位用来指示线程是否需要等待 private boolean okToProceed; public MissedNotifyFix() { print("in MissedNotify()"); proceedLock = new Object(); //先设置为false okToProceed = false; } public void waitToProceed() throws InterruptedException { print("in waitToProceed() - entered"); synchronized ( proceedLock ) { print("in waitToProceed() - entered sync block"); //while循环判断,这里不用if的原因是为了防止早期通知 while ( okToProceed == false ) { print("in waitToProceed() - about to wait()"); proceedLock.wait(); print("in waitToProceed() - back from wait()"); } print("in waitToProceed() - leaving sync block"); } print("in waitToProceed() - leaving"); } public void proceed() { print("in proceed() - entered"); synchronized ( proceedLock ) { print("in proceed() - entered sync block"); //通知之前,将其设置为true,这样即使出现通知遗漏的情况,也不会使线程在wait出阻塞 okToProceed = true; print("in proceed() - changed okToProceed to true"); proceedLock.notifyAll(); print("in proceed() - just did notifyAll()"); print("in proceed() - leaving sync block"); } print("in proceed() - leaving"); } private static void print(String msg) { String name = Thread.currentThread().getName(); System.out.println(name + ": " + msg); } public static void main(String[] args) { final MissedNotifyFix mnf = new MissedNotifyFix(); Runnable runA = new Runnable() { public void run() { try { //休眠1000ms,大于runB中的500ms, //是为了后调用waitToProceed,从而先notifyAll,后wait, Thread.sleep(1000); mnf.waitToProceed(); } catch ( InterruptedException x ) { x.printStackTrace(); } } }; Thread threadA = new Thread(runA, "threadA"); threadA.start(); Runnable runB = new Runnable() { public void run() { try { //休眠500ms,小于runA中的1000ms, //是为了先调用proceed,从而先notifyAll,后wait, Thread.sleep(500); mnf.proceed(); } catch ( InterruptedException x ) { x.printStackTrace(); } } }; Thread threadB = new Thread(runB, "threadB"); threadB.start(); try { Thread.sleep(10000); } catch ( InterruptedException x ) {} print("about to invoke interrupt() on threadA"); threadA.interrupt(); }
}
执行结果如下:
注意代码中加了注释的部分,在threadB进行通知之前,先将okToProceed置为true,这样如果threadA将通知遗漏,那么就不会进入while循环,也便不会执行wait方法,线程也就不会阻塞。如果通知没有被遗漏,wait方法返回后,okToProceed已经被置为true,下次while循环判断条件不成立,便会退出循环。
这样,通过标志位和wait、notifyAll的配合使用,便避免了通知遗漏而造成的阻塞问题。
总结:在使用线程的等待/通知机制时,一般都要配合一个boolean变量值(或者其他能够判断真假的条件),在notify之前改变该boolean变量的值,让wait返回后能够退出while循环(一般都要在wait方法外围加一层while循环,以防止早期通知),或在通知被遗漏后,不会被阻塞在wait方法处。这样便保证了程序的正确性。
转载于:https://www.cnblogs.com/zailushang1996/p/8796060.html
【Java并发编程】之十一:线程间通信中notify通知的遗漏相关推荐
- Java并发编程(04):线程间通信,等待/通知机制
本文源码:GitHub·点这里 || GitEE·点这里 一.概念简介 1.线程通信 在操作系统中,线程是个独立的个体,但是在线程执行过程中,如果处理同一个业务逻辑,可能会产生资源争抢,导致并发问题, ...
- 【JUC并发编程03】线程间通信
文章目录 3 线程间通信 3.1 synchronized 实现案例 3.2 虚假唤醒问题 3.3 Lock 实现案例 3 线程间通信 线程间通信有两种实现方法: 关键字 synchronized 与 ...
- Java并发编程--6.Exchanger线程间交换数据
在两个线程之间定义同步点,当两个线程都到达同步点时,他们交换数据结构,因此第一个线程的数据结构进入到第二个线程中,第二个线程的数据结构进入到第一个线程中 在生产者-消费者情境模式中它包含了一个数缓冲区 ...
- 【Java从零到架构师第1季】【并发 Concurrent 03】线程间通信_ReentrantLock_线程池
持续学习&持续更新中- 守破离 [Java从零到架构师第1季][并发 Concurrent 03]线程间通信_ReentrantLock_线程池 线程间通信 线程间通信-示例 可重入锁Reen ...
- java并发编程实践(2)线程安全性
[0]README 0.0)本文部分文字描述转自:"java并发编程实战", 旨在学习"java并发编程实践(2)线程安全性" 的相关知识: 0.1)几个术语( ...
- Java进阶知识 - 多线程与线程间通信
CountdownLatch, CyclicBarrier 分别适合什么场景呢? 大部分情况下, 子线程只需要关心自身执行的任务. 但在某些复杂的情况下, 需要使用多个线程来协同完成某个任务, 这就涉 ...
- java并发编程第一课 线程的创建、停止和状态变更
开篇词: 由点及面,搭建你的 Java 并发知识网 你好,欢迎学习<Java 并发编程核心 78 讲>,我是讲师星星,一线互联网公司资深研发工程师,参与过集团内多个重点项目的设计与开发. ...
- Java基础学习——多线程(线程间通信-生产者消费者代码示例)
JDK 1.5提供了多线程升级方案 将同步synchronized替换成了显示的Lock操作.可以实现唤醒.冻结指定的线程. Lock接口 Lock 实现提供了比使用 synchronized 方法和 ...
- Java并发编程—什么是线程?
原文作者:way_more 原文地址:Java 多线程常见基础面试题总结,面试必看! 目录 一.什么是线程和进程? 二.简要描述线程与进程的关系 三.FAQ 一.什么是线程和进程? 1.1. 何为进程 ...
最新文章
- golang中的sync.WaitGroup
- GAN生成对抗网络-GAN原理与基本实现-去噪与卷积自编码器01
- 300来行代码实现最小Linux文件系统
- OpenGL+VS2013+WIN7(64)组态
- LeetCode(530)——二叉搜索树的最小绝对差(JavaScript)
- 我的世界java版_我的世界Java版1.15版本
- mysql比较两个表中count_mysql两个表统计查询问题?
- 1.1 OC类的认识
- 使用 kotlin 开发 android 遇到的问题
- Java 学习路线一条龙版
- Redisson分布式锁学习总结:RedissonMultiLock 如何同时锁住N个资源
- 阿里云盾(云安全)是什么?有什么作用?
- iphone xr xs_如何关闭iPhone X,XS和XR
- 阿里MNN推理框架相关介绍
- Retrofit简单使用(小白都会了)
- 离散数学复习三:广义交、广义并、蕴含式的真值表
- 计算机产业能否迅速发展,工业计算机得到了迅速的发展和全面的普及
- Windows下使用任务计划程序实现宽带开机自动拨号和断线自动重连
- STC15F104E (8脚单片机)实现蓝牙串口控制舵机转向
- 养成良好的习惯比投资知识更重要
热门文章
- [ActionScript 3.0] 获取TextFiled字符边框
- ASP.NET MVC+EF框架+EasyUI实现权限管理系列(18)-过滤器的使用和批量删除数据(伪删除和直接删除)...
- [詹兴致矩阵论习题参考解答]习题1.13
- 简单谈谈haskell
- idea 控制台程序的打包
- sftp方式从windows上传文件到Linux服务器
- 面试题(一)—Java基础(上)
- HM 内存池设计(2) HM6.0内存池设计
- 关于在群集环境下UDDI Web应用程序组件配置失败问题
- (转)深入理解Javascript闭包(closure)