并发编程大师系列之: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编码这一篇文章就够了
概述 在使用Python或者其他的编程语言,都会多多少少遇到编码错误,处理起来非常痛苦.在Stack Overflow和其他的编程问答网站上,UnicodeDecodeError和UnicodeEnc ...
- 【程序人生】程序员真会玩,工作996,生病ICU
昨天Github上一个项目彻底爆红了网络,短短一天star数突破一万,Issues已破1800,大家纷纷说出有关企业的不合理加班行为,句句吐露程序员的心声,掀起了一波抵制加班潮,抵制996. 该项目里 ...
- Angular 学习笔记 (Angular 9 & ivy)
refer : https://blog.angularindepth.com/all-you-need-to-know-about-ivy-the-new-angular-engine-9cde47 ...
- C#用户控件实战01_CSS布局
很多应用系统的主页布局,一般采用如下案例所示布局较多,如下图的CSS布局框架,上.中.下,接下来我们演示,在C#中实现如下的业务架构布局. 代码范例: 在<body></body&g ...
- 浅学CLR via C#笔记之类型转换
我们都知道CLR最重要的一个特性就是类型安全,它在运行时就知道对象类型. 但我们会经常用到将一种类型转换成另一种类型,CLR也允许将对象转成他的实际类型,或者是它的基类型. 在C#中,支持隐士转换成它 ...
- module 'cv2' has no attribute 'KNearest_create'
python版本:3.6.5 opencv版本:3.2.0 使用的是jupyter notebook 源代码如下: import cv2 import numpy as np import matpl ...
- 数据结构与算法(周测3-Huffman树)
判断题 1.Given a Huffman tree for N (≥2) characters, all with different weights. The weight of any non- ...
- MySQL库的相关操作
再熟悉一下Mysql库.表.记录的基本操作. 库 增 create database userinfo1 charset utf8; 查 show databases; show create dat ...
- HBase分布式搭建常见错误
[root@node001 bin]# hbase shell SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found bin ...
- MYSQL安装与卸载(一)
系统:win10(其他版本系统不在本次内容) MYSQL下载地址:https://dev.mysql.com/downloads/mysql/ MySQL安装主流分为两种:msi,zip Zip:压缩 ...