Java并发编程的艺术笔记(二)——wait/notify机制
一.概述
一个线程修改了一个对象的值,另一个线程感知到变化从而做出相应的操作。前者是生产者,后者是消费者。
等待/通知机制,是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。上述两个线程通过对象O来完成交互,而对象上的wait()和notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。
二.代码示例
package concurrent;
import java.util.concurrent.TimeUnit;
public class WaitNotifyDemo2 {
static final Object obj = new Object();
private static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
Thread produce = new Thread(new Produce(), "生产者线程");
Thread consume = new Thread(new Consume(), "消费者线程");
consume.start();
Thread.sleep(1000);
produce.start();
}
//生产者线程
static class Produce implements Runnable {
@Override
public void run() {
//加锁,拥有lock的monitor
synchronized (obj) {
System.out.println("进入生产者线程");
System.out.println("生产");
try {
//模拟生产
TimeUnit.MILLISECONDS.sleep(2000);
//改变条件
flag = true;
// 获取lock的锁,然后进行通知等待在obj上的对象,通知时不会释放lock的锁,
// 直到当前线程释放了lock后,WaitThread才能从wait方法中返回
obj.notifyAll();
TimeUnit.MILLISECONDS.sleep(1000); //模拟其他耗时操作
System.out.println("退出生产者线程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消费者线程
static class Consume implements Runnable {
@Override
public void run() {
// 加锁,拥有lock的Monitor
synchronized (obj) {
System.out.println("进入消费者线程");
System.out.println("wait flag 1:" + flag);
// 当条件不满足时,继续wait,同时释放了lock的锁
while (! flag) {
System.out.println("还没生产,进入等待");
try {
//等待,线程被阻塞
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束等待");
}
//满足条件,消费
System.out.println("wait flag 2:" + flag);
System.out.println("消费");
System.out.println("退出消费者线程");
}
}
}
}
打印如下:
进入消费者线程
wait flag 1:false
还没生产,进入等待
进入生产者线程
生产
退出生产者线程
结束等待
wait flag 2:true
消费
退出消费者线程
对这一过程的详解:

在图4-3中,WaitThread首先获取了对象的锁,然后调用对象的wait()方法,从而放弃了锁并进入了对象的等待队列WaitQueue中,进入等待状态。由于WaitThread释放了对象的锁,NotifyThread随后获取了对象的锁,并调用对象的notify()方法,将WaitThread从WaitQueue移到SynchronizedQueue中,此时WaitThread的状态变为阻塞状态。NotifyThread释放了锁之后,WaitThread再次获取到锁并从wait()方法返回继续执行。
三.等待/通知机制范式的提炼
等待方遵循如下原则。
1)获取对象的锁。
2)如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件。
3)条件满足则执行对应的逻辑。
对应的伪代码如下。
synchronized(对象) {
while(条件不满足) {
对象.wait();
}
对应的处理逻辑
}
通知方遵循如下原则。
1)获得对象的锁。
2)改变条件。
3)通知所有等待在对象上的线程。
对应的伪代码如下。
synchronized(对象) {
改变条件
对象.notifyAll();
}
四.为什么要使用while循环/notifyAll
1.为什么用while:wait()的线程永远不能确定其他线程会在什么状态下notify(),所以必须在被唤醒、抢占到锁并且从wait()方法退出的时候再次进行指定条件的判断,以决定是满足条件往下执行呢还是不满足条件再次wait()呢?多个线程消费,那么就极有可能出现唤醒生产者的是另一个生产者或者唤醒消费者的是另一个消费者,这样的情况下用if就必然会现类似过度生产或者过度消费的情况了
2.使用notifyAll而不是notify
notify适用于只有一个生产者和一个消费者的情况,如果是两个生产者两个消费者,notify()导致死锁,需使用notifyAll()
Java并发编程的艺术笔记(二)——wait/notify机制的更多相关文章
- Java并发编程实战 读书笔记(二)
关于发布和逸出 并发编程实践中,this引用逃逸("this"escape)是指对象还没有构造完成,它的this引用就被发布出去了.这是危及到线程安全的,因为其他线程有可能通过这个 ...
- 多线程的通信和同步(Java并发编程的艺术--笔记)
1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递. 2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...
- Java并发编程的艺术笔记(七)——CountDownLatch、CyclicBarrier详解
一.等待多线程完成的CountDownLatch CountDownLatch允许一个或多个线程等待其他线程完成操作,像加强版的join.(t.join()是等待t线程完成) 例: (1)开启多个线程 ...
- Java并发编程的艺术笔记(五)——Java中的锁
一.Lock接口的几个功能: 显示的获取和释放锁 尝试非阻塞的获取锁 能被中断的获取锁 超时获取锁 使用方式: Lock lock = new ReentrantLock(); lock.lock() ...
- Java并发编程的艺术笔记(九)——FutureTask详解
FutureTask是一种可以取消的异步的计算任务.它的计算是通过Callable实现的,多用于耗时的计算. 一.FutureTask的三种状态 二.get()和cancel()执行示意 三.使用 一 ...
- Java并发编程的艺术(十二)——并发容器和框架
ConcurrentHashMap 为什么需要ConcurrentHashMap HashMap线程不安全,因为HashMap的Entry是以链表的形式存储的,如果多线程操作可能会形成环,那样就会死循 ...
- Java并发编程的艺术· 笔记(1)
目录 1.volatile的原理 2.Synchonized 3.无锁-偏向锁-轻量级锁-重量级锁 4.Java实现原子操作 1.volatile的原理 如何保持可见性: 1)将当前处理器缓存行的数据 ...
- Java并发编程的艺术(十二)——线程安全
1. 什么是『线程安全』? 如果一个对象构造完成后,调用者无需额外的操作,就可以在多线程环境下随意地使用,并且不发生错误,那么这个对象就是线程安全的. 2. 线程安全的几种程度 线程安全性的前提:对『 ...
- Java并发编程的艺术笔记(八)——线程池
一.线程池的主要处理流程 ThreadPoolExecutor执行execute方法分下面4种情况. 1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步需要获 ...
随机推荐
- vlang
参考 V语言中文教程 - 基础部分
- 运维LVS三种模式十种调度算法
一.LVS简介 LVS(Linux Virtual Server)即Linux虚拟服务器,是由章文嵩博士主导的开源负载均衡项目,目前LVS已经被集成到Linux内核模块中.该项目在Linux内核中实现 ...
- css 超出部分显示省略号 汇总
单行: 加宽度 overflow: hidden; text-overflow:ellipsis; white-space: nowrap; 多行: display: -webkit-box; -we ...
- this 到底指向谁
this 的指向,是在调用函数时根据执行上下文所动态确定的. 在函数体中,简单调用该函数时(非显式/隐式绑定下),严格模式下 this 绑定到 undefined,否则绑定到全局对象 window/g ...
- MWPhotoBrowser.bundle: bundle format unrecognized, invalid, or unsuitable
今天在github下载了MWPhotoBrowser的demo想跑一下,却发现报了MWPhotoBrowser.bundle: bundle format unrecognized, invalid, ...
- 3U VPX 加固智能计算异构服务器
3U VPX 加固智能计算异构服务器 北京太速科技有限公司在线客服:QQ:448468544 公司网站:www.orihard.com联系电话:15084122580
- 【c#】ADO操作Access的mdb数据库只能读不能修改的解决方法
在使用ACCESS数据库时连接字符串如 string strcon=@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=F:\Access操作\简易 ...
- [DllImport("kernel32.dll")]使用
C#中读取ini配置文件 [DllImport("kernel32.dll")] using System;using System.Collections.Generic;usi ...
- libpng Cximage图片处理
跨平台 开源 png图片处理 https://www.cnblogs.com/lidabo/p/6923426.html Cximage BIPro
- ZROI 19.08.02 计算几何
1.向量基础知识 \(atan2\)可以求极角,但是不是特别精确,在坐标接近\(10^{9}\)时会出锅,安全的做法是叉积. 旋转.反射和平移等都可以抽象为矩阵,即,它们可以复合.(需要一些必修四知识 ...