并发编程大师系列之: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
随机推荐
- 有助提升编程的几个Python 技巧
一行代码定义List 定义某种列表时,写For 循环过于麻烦,幸运的是,Python有一种内置的方法可以在一行代码中解决这个问题. 下面是使用For循环创建列表和用一行代码创建列表的对比. x = [ ...
- python爬虫-豆瓣电影的尝试
一.背景介绍 1. 使用工具 Pycharm 2. 安装的第三方库 requests.BeautifulSoup 2.1 如何安装第三方库 File => Settings => Proj ...
- JDBC预编译statement(preparedstatement)和statement的比较、execute与executeUpdate的区别
和 Statement一样,PreparedStatement也是用来执行sql语句的与创建Statement不同的是,需要根据sql语句创建PreparedStatement除此之外,还能够通过设置 ...
- C++中深拷贝与浅拷贝
浅拷贝和深拷贝 在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B.这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指 ...
- Git config 使用说明(转)
原文:https://blog.csdn.net/gdutxiaoxu/article/details/79253737
- Spring Cloud Alibaba学习笔记(5) - 整合Sentinel及Sentinel规则
整合Sentinel 应用整合Sentinel 在dependencies中添加依赖,即可整合Sentinel <dependency> <groupId>com.alibab ...
- 什么是RAID(磁盘阵列)
RAID全称Redundant Array of Independent Disk,即独立冗余磁盘阵列.RAID技术由加州大学伯克利分校1987年提出,最初是为了组合小的廉价磁盘来代替大的昂贵磁盘,同 ...
- C# 卡控时间输入
int hour = (int.Parse(DateTime.Now.Hour.ToString())) * 60; int minute = int.Parse(DateTime.No ...
- 同步IO,异步IO,阻塞,非阻塞的定义与区别
异步I/O 是指用户程序发起IO请求后,不等待数据,同时操作系统内核负责I/O操作把数据从内核拷贝到用户程序的缓冲区后通知应用程序.数据拷贝是由操作系统内核完成,用户程序从一开始就没有等待数据,发起请 ...
- UI5-技术篇-SAP UI5数据表进行了比较:sap.m.Table与sap.ui.table.Table
https://a.kabachnik.info/sap.m.table-vs-sap.ui.table.table-features-compared.html SAP UI5数据表进行了比较:sa ...