并发编程大师系列之:wait/notify/notifyAll/condition
1. wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。
2. 调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)。
3. 调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程。
4. 调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程。
5. 如果调用某个对象的wait()方法,当前线程必须拥有这个对象的锁,因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
6. 调用某个对象的wait()方法,相当于让当前线程交出此对象的锁,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁);
7. notify()方法能够唤醒一个正在等待该对象锁的线程,当有多个线程都在等待该对象的锁的话,则只能唤醒其中一个线程,具体唤醒哪个线程由虚拟机确定。
8. nofityAll()方法能够唤醒所有正在等待该对象的monitor的线程,这一点与notify()方法是不同的。
*** 一个线程被唤醒不代表立即获取了对象的monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。 ***
例子:
/**
* @author 70KG
* @Title: Test02
* @Description: test
* @date 2018/7/5下午9:49
*/
public class Test02 { public static Object object = new Object(); public static void main(String[] args) { // 启动两个线程
Thread thread1 = new Thread1("1号");
Thread thread2 = new Thread2("2号"); thread1.start(); try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
} thread2.start();
} // 线程1,处于等待状态
static class Thread1 extends Thread { Thread1(String name) {
this.setName(name);
} @Override
public void run() {
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
}
System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁");
}
}
} // 线程2调用notify
static class Thread2 extends Thread { Thread2(String name) {
this.setName(name);
} @Override
public void run() {
synchronized (object) {
object.notify();
System.out.println("线程" + Thread.currentThread().getName() + "调用了object.notify()");
}
System.out.println("线程" + Thread.currentThread().getName() + "释放了锁");
}
} }
结果都是一样的,验证了上面的内容。
线程2号调用了object.notify()
线程2号释放了锁
线程1号获取到了锁
notify和notifyAll例子:
调用wait方法必须在同步块中进行。用线程来监听快递的信息,包括里程数的变化和地点的变化。
/**
* @author 70KG
* @Title: Express
* @Description: 快递类
* @date 2018/7/4下午10:27
*/
public class Express { // 始发地
private final static String CITY = "ShangHai"; // 里程变化
private int km; // 地点变化
private String site; Express() { } Express(int km, String site) {
this.km = km;
this.site = site;
} // 里程数变化,会唤起线程
public synchronized void changeKm() {
this.km = 101;
notify();
} // 地点变化会唤起线程
public synchronized void changeSite() {
this.site = "BeiJing";
notify();
} // 用来监听里程数的变化
public synchronized void waitKm() {
while (this.km <= 100) {
try {
wait();
System.out.println(Thread.currentThread().getId() + "-号监听===里程变化===的线程被唤醒了。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getId() + "-号监听===里程变化===的线程去做相应的事了");
} // 用来监听地点的变化
public synchronized void waitSite() {
while (CITY.equals(this.site)) {
try {
wait();
System.out.println(Thread.currentThread().getId() + "-号监听===地点变化===的线程被唤醒了。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getId() + "-号监听===地点变化===的线程去做相应的事了");
} }
/**
* @author itachi
* @Title: Test
* @Description: 测试
* @date 2018/7/4下午10:40
*/
public class Test { // 初始化快递
private static Express express = new Express(0, "ShangHai"); // 用来监听里程数变化的线程
static class CheckKm implements Runnable {
@Override
public void run() {
express.waitKm();
}
} // 用来监听地点变化的线程
static class CheckSite implements Runnable {
@Override
public void run() {
express.waitSite();
}
} public static void main(String[] args) throws InterruptedException{
// 启动三个线程去监听里程数的变化
for (int i = 0; i <= 2; i++) {
new Thread(new CheckKm()).start();
} // 启动三个线程去监听地点的变化
for (int i = 0; i <= 2; i++) {
new Thread(new CheckSite()).start();
} // 主线程睡眠一秒,异常信息抛出去
Thread.sleep(1000); // 让快递的地点发生变化
express.changeSite();
} }
结果:
可见虽然是让地点发生了变化,但却随机唤醒了一个监听里程数变化的线程,并且使用整个程序处于无限等待状态。
9-号监听===里程变化===的线程被唤醒了。。。
如果将notify换成notifyAll的话,运行结果:
三个监视地点的线程都被唤醒了,各自去做各自的事情了,未被唤醒的用来监听里程数变化的线程依然处于监听状态,因为它的里程数没有变化。
14-号监听===地点变化===的线程被唤醒了。。。
14-号监听===地点变化===的线程去做相应的事了
13-号监听===地点变化===的线程被唤醒了。。。
13-号监听===地点变化===的线程去做相应的事了
12-号监听===地点变化===的线程被唤醒了。。。
12-号监听===地点变化===的线程去做相应的事了
11-号监听===里程变化===的线程被唤醒了。。。
10-号监听===里程变化===的线程被唤醒了。。。
9-号监听===里程变化===的线程被唤醒了。。。
总结起来等待和通知的标准范式:
等待方:
1. 获取对象的锁
2. 循环里面判断条件是否满足,不满足调用wait方法继续等待
3. 条件满足的话就去执行相应的业务逻辑
通知方:
1. 获取对象的锁
2. 改变条件
3. 通知所有等待在对象上的线程
并发编程大师系列之:wait/notify/notifyAll/condition的更多相关文章
- 并发编程大师系列之:Synchronized的类锁和对象锁
说到并发编程,感觉跟大多数人一样,谈之色变,说它简单把,其实很有内容,说难吧,用起来也挺容易,最近我硬着头皮,决心要把并发编程好好的搞一遍.以前,面试的时候,面试官问,并发编程会吗?嗯,接触过,就加一 ...
- 【Java并发编程】:使用wait/notify/notifyAll实现线程间通信
在java中,可以通过配合调用Object对象的wait()方法和notify()方法或notifyAll()方法来实现线程间的通信.在线程中调用wait()方法,将阻塞等待其他线程的通知(其他线程调 ...
- 并发编程大师系列之:CountDownLatch和Join
业务场景描述:假设一条流水线上有三个工作者:worker1,worker2,worker3.有一个任务的完成需要他们三者协作完成,worker3可以开始这个任务的前提是worker1和worker2完 ...
- 并发编程大师系列之:线程的定义和中断 interrupt
1.启动线程的三种方式: 1.1继承Thread类 public static class UseThread extends Thread { public void run() { System. ...
- Java并发编程锁系列之ReentrantLock对象总结
Java并发编程锁系列之ReentrantLock对象总结 在Java并发编程中,根据不同维度来区分锁的话,锁可以分为十五种.ReentranckLock就是其中的多个分类. 本文主要内容:重入锁理解 ...
- 并发编程JUC系列AQS(CountDownLatch、CyclicBarrier、Semaphore)
一.CountDownLatch package com.jonychen.test; import java.util.concurrent.CountDownLatch; import java. ...
- Java并发编程原理与实战二十三:Condition原理分析
先来回顾一下java中的等待/通知机制 我们有时会遇到这样的场景:线程A执行到某个点的时候,因为某个条件condition不满足,需要线程A暂停:等到线程B修改了条件condition,使condit ...
- 并发编程基础之wait以及notify的用法
一:概念 线程通信中经常用到wait和notify,顾名思义,wait即让当前线程处于等待状态,notify通知锁对象 上的另一个线程被唤醒,这里的唤醒是指可以去争夺锁资源,nofityAll是唤醒该 ...
- Java并发编程(详解wait(), notify(),sleep())
http://blog.csdn.net/luckyzhoustar/article/details/48179161
随机推荐
- social engineering toolkit
1. freebuf介绍 http://www.freebuf.com/sectool/73409.html 2. github https://github.com/trustedsec/socia ...
- Rust零碎总结
1.Rust里没有null的概念,但是实际上有很多地方是需要null的概念的,这个时候就可以用Option来代替,它是泛型T的一个包装类,就是C#里的int?或Java里的Optional: [但反序 ...
- (一)Spring Security Demo 登陆与退出
文章目录 配置springSecurityFilterChain过滤器 配置身份验证 加载配置 登陆项目 退出 下面的代码需要spring环境的支持: 看这个系列博客之前,需要这个博客,大概了解下 s ...
- c++中继承的使用
1.c++中继承有公有继承,保护继承,私有继承 定义个基类: #include using namespace std; class Base { public: void display() { c ...
- Python26之字典2(内置函数)
一.工厂函数的概念 和序列类型的工厂函数一样,dict()也是一个工厂函数,本质上是一个类,Python程序无处不对象的概念可见一斑 二.字典类型内置函数的用法 1.fromkeys(iterable ...
- WUSTOJ 1339: 土豪fcbruce(Java)
题目链接:1339: 土豪fcbruce Description 10年后,就职于Google的fcbruce赞助了武汉科技大学好多钱,学校因此决定扩建.第一步是新建宿舍楼,为了整洁美观,fcbruc ...
- 嵌入式Linux学习笔记之第二阶段---文件I/O
1.文件IO的四个函数 一些术语: 不带缓冲的I/O: 每个read和write都调用内核中的一个系统调用. 文件描述符: 一个非负整数,对内核而言,所以打开的文件都通过文件描述符引用. ①打开或创建 ...
- SQL Server2008 删除重复记录只剩一条(无Uid)
INSERT INTO 表1 SELECT * FROM 视图1 CREATE TABLE ##TMP01 ---创建 ...
- sql 语句实现一串数字位数不足在左侧补0的技巧
https://www.cnblogs.com/mylydg/p/5725189.html 在日常使用sql做查询插入操作时,我们通常会用到用sql查询一串编号,这串编号由数字组成.为了统一美观,我们 ...
- solr的post.jar
http://iamyida.iteye.com/blog/2207920 跟益达学Solr5之玩转post.jar