并发编程大师系列之: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
随机推荐
- mac 上更改环境变量
第一次配置Mac的环境变量,到网上转了一圈才找到正确方法. 打开终端,新建.bash_profile文件在~/目录下(如果电脑里已经有了这个文件,跳过这一步) touch ~/.bash_profil ...
- python线程障碍对象Barrier(34)
python线程Barrier俗称障碍对象,也称栅栏,也叫屏障. 一.线程障碍对象Barrier简介 # 导入线程模块 import threading # 障碍对象barrier barrier = ...
- 最详细的maven教程
转载 https://blog.csdn.net/wymrdjm/article/details/78695956 所有用Maven管理的真实的项目都应该是分模块的,每个模块都对应着一个pom.x ...
- K8S从入门到放弃系列-(13)Kubernetes集群mertics-server部署
集群部署好后,如果我们想知道集群中每个节点及节点上的pod资源使用情况,命令行下可以直接使用kubectl top node/pod来查看资源使用情况,默认此命令不能正常使用,需要我们部署对应api资 ...
- 多线程(8) — ThreadLocal
ThreadLocal是一个线程的局部变量,也就是只有当前线程可以访问,是线程安全的.为每一个线程分配不同的对象,需要在应用层面保证ThreadLocal只起到简单的容器作用. ThreadLocal ...
- Apache Rewrite 规则详解知识大全
Rewrite是一种服务器的重写脉冲技术,它可以使得服务器可以支持 URL 重写,是一种最新流行的服务器技术.它还可以实现限制特定IP访问网站的功能. 1.Rewrite标志 R[=code](for ...
- Python开发【第一章】:简介和入门
Python简介 Python的创始人为Guido van Rossum.1989年圣诞节期间,在阿姆斯特丹,Guido为了打发圣诞节的无趣,决心开发一个新的脚本解释程序,做为ABC 语言的一种继承. ...
- 1. Spark基础解析
1.1 Spark概述 1.1.1 什么是Spark 官网:http://spark.apache.org Spark是一种快速.通用.可扩展的大数据分析引擎,2009年诞生于加州大学伯克利分校AMP ...
- Docker 学习笔记(二):Dockerfile 定制镜像
镜像的定制实际上就是定制每一层所添加的配置.文件. 如果我们可以把每一层修改.安装.构建.操作的命令都写入一个脚本,用这个脚本来构建.定制镜像,那么之前提及的无法重复的问题.镜像构建透明性的问题.体积 ...
- NIO-FileChannel源码分析
目录 NIO-FileChannel源码分析 目录 前言 RandomAccessFile 接口 创建实例 获取文件通道 FileChannelImpl 创建 写文件 读文件 修改起始位置 获取文件长 ...