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的更多相关文章

  1. 并发编程大师系列之:Synchronized的类锁和对象锁

    说到并发编程,感觉跟大多数人一样,谈之色变,说它简单把,其实很有内容,说难吧,用起来也挺容易,最近我硬着头皮,决心要把并发编程好好的搞一遍.以前,面试的时候,面试官问,并发编程会吗?嗯,接触过,就加一 ...

  2. 【Java并发编程】:使用wait/notify/notifyAll实现线程间通信

    在java中,可以通过配合调用Object对象的wait()方法和notify()方法或notifyAll()方法来实现线程间的通信.在线程中调用wait()方法,将阻塞等待其他线程的通知(其他线程调 ...

  3. 并发编程大师系列之:CountDownLatch和Join

    业务场景描述:假设一条流水线上有三个工作者:worker1,worker2,worker3.有一个任务的完成需要他们三者协作完成,worker3可以开始这个任务的前提是worker1和worker2完 ...

  4. 并发编程大师系列之:线程的定义和中断 interrupt

    1.启动线程的三种方式: 1.1继承Thread类 public static class UseThread extends Thread { public void run() { System. ...

  5. Java并发编程锁系列之ReentrantLock对象总结

    Java并发编程锁系列之ReentrantLock对象总结 在Java并发编程中,根据不同维度来区分锁的话,锁可以分为十五种.ReentranckLock就是其中的多个分类. 本文主要内容:重入锁理解 ...

  6. 并发编程JUC系列AQS(CountDownLatch、CyclicBarrier、Semaphore)

    一.CountDownLatch package com.jonychen.test; import java.util.concurrent.CountDownLatch; import java. ...

  7. Java并发编程原理与实战二十三:Condition原理分析

    先来回顾一下java中的等待/通知机制 我们有时会遇到这样的场景:线程A执行到某个点的时候,因为某个条件condition不满足,需要线程A暂停:等到线程B修改了条件condition,使condit ...

  8. 并发编程基础之wait以及notify的用法

    一:概念 线程通信中经常用到wait和notify,顾名思义,wait即让当前线程处于等待状态,notify通知锁对象 上的另一个线程被唤醒,这里的唤醒是指可以去争夺锁资源,nofityAll是唤醒该 ...

  9. Java并发编程(详解wait(), notify(),sleep())

    http://blog.csdn.net/luckyzhoustar/article/details/48179161

随机推荐

  1. FutureTask源码阅读

    FutureTask功能用法 类结构 源码中详细说明了FutureTask生命周期状态及变化 /** * The run state of this task, initially NEW. The ...

  2. 剑指offer48:不用加减乘除做加法

    1 题目描述 写一个函数,求两个整数之和,要求在函数体内不得使用+.-.*./四则运算符号. 2 思路和方法 位运算符:两个数异或(^)[1^0=1, 1^1=0, 0^0=0, 0^1=1, 5^5 ...

  3. Python中使用列表的一部分——参考Python编程从入门到实践

    处理列表中的部分元素——切片 1. 切片 players = ['charles', 'martina', 'michael', 'florence', 'eli'] print(players[0: ...

  4. PAT(B) 1083 是否存在相等的差(Java)统计

    题目链接:1083 是否存在相等的差 (20 point(s)) 题目描述 给定 N 张卡片,正面分别写上 1.2.--.N,然后全部翻面,洗牌,在背面分别写上 1.2.--.N.将每张牌的正反两面数 ...

  5. WUSTOJ 1307: 校门外的树(Java)

    题目链接:

  6. docker 实践九:docker swarm

    介绍了 docker 三剑客中的 docker-machine 和 docker-compose 之后,就剩下一个 docker swarm 了.那本篇的主角就是它了. 注:环境为 CentOS7,d ...

  7. Springboot入门及配置文件介绍(内置属性、自定义属性、属性封装类)

    目的: 1.Springboot入门 SpringBoot是什么? 使用Idea配置SpringBoo使用t项目 测试案例 2.Springboot配置文件介绍 内置属性 自定义属性 属性封装类 Sp ...

  8. 单选按钮(CheckBox)

    import React, { useState, useEffect } from 'react' import PropTypes from 'prop-types' import _ from ...

  9. linux安装png2icon方法

    此工具用于将png图片转换为ico格式的文件,一个小工具,但很实用 官网:http://www.winterdrache.de/freeware/png2ico/ 下载: wget http://ww ...

  10. C#在DataTable中使用LINQ

    LINQ 查询适用于实现的数据源 IEnumerable<T>接口或System.Query.IQueryable接口. DataTable类默认是没有实现以上接口的. 所以要在DataT ...