JAVA线程同步 (一)wait(), notify()和notifyAll()使用
wait(),notify()和notifyAll()都是java.lang.Object的方法:
wait(): Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
notify(): Wakes up a single thread that is waiting on this object's monitor.
notifyAll(): Wakes up all threads that are waiting on this object's monitor.
这三个方法,都是Java语言提供的实现线程间阻塞(Blocking)和控制进程内调度(inter-process communication)的底层机制。在解释如何使用前,先说明一下两点:
1. 正如Java内任何对象都能成为锁(Lock)一样,任何对象也都能成为条件队列(Condition queue)。而这个对象里的wait(), notify()和notifyAll()则是这个条件队列的固有(intrinsic)的方法。
2. 一个对象的固有锁和它的固有条件队列是相关的,为了调用对象X内条件队列的方法,你必须获得对象X的锁。这是因为等待状态条件的机制和保证状态连续性的机制是紧密的结合在一起的。
(An object's intrinsic lock and its intrinsic condition queue are related: in order to call any of the condition queue methods on object X, you must hold the lock on X. This is because the mechanism for waiting for state-based conditions is necessarily tightly bound to the mechanism fo preserving state consistency)
根据上述两点,在调用wait(), notify()或notifyAll()的时候,必须先获得锁,且状态变量须由该锁保护,而固有锁对象与固有条件队列对象又是同一个对象。也就是说,要在某个对象上执行wait,notify,先必须锁定该对象,而对应的状态变量也是由该对象锁保护的。
知道怎么使用后,我们来问下面的问题:
1. 执行wait, notify时,不获得锁会如何?
请看代码:
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
obj.wait();
obj.notifyAll();
}
执行以上代码,会抛出java.lang.IllegalMonitorStateException的异常。
2. 执行wait, notify时,不获得该对象的锁会如何?
请看代码:

public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Object lock = new Object();
synchronized (lock) {
obj.wait();
obj.notifyAll();
}
}

执行代码,同样会抛出java.lang.IllegalMonitorStateException的异常。
3. 为什么在执行wait, notify时,必须获得该对象的锁?
这是因为,如果没有锁,wait和notify有可能会产生竞态条件(Race Condition)。考虑以下生产者和消费者的情景:
1.1生产者检查条件(如缓存满了)-> 1.2生产者必须等待
2.1消费者消费了一个单位的缓存 -> 2.2重新设置了条件(如缓存没满) -> 2.3调用notifyAll()唤醒生产者
我们希望的顺序是: 1.1->1.2->2.1->2.2->2.3
但在多线程情况下,顺序有可能是 1.1->2.1->2.2->2.3->1.2。也就是说,在生产者还没wait之前,消费者就已经notifyAll了,这样的话,生产者会一直等下去。
所以,要解决这个问题,必须在wait和notifyAll的时候,获得该对象的锁,以保证同步。
请看以下利用wait,notify实现的一个生产者、一个消费者和一个单位的缓存的简单模型:

public class QueueBuffer {
int n;
boolean valueSet = false;
synchronized int get() {
if (!valueSet)
try {
wait();
} catch (InterruptedException e) {
System.out.println("InterruptedException caught");
}
System.out.println("Got: " + n);
valueSet = false;
notify();
return n;
}
synchronized void put(int n) {
if (valueSet)
try {
wait();
} catch (InterruptedException e) {
System.out.println("InterruptedException caught");
}
this.n = n;
valueSet = true;
System.out.println("Put: " + n);
notify();
}
}


public class Producer implements Runnable {
private QueueBuffer q;
Producer(QueueBuffer q) {
this.q = q;
new Thread(this, "Producer").start();
}
public void run() {
int i = 0;
while (true) {
q.put(i++);
}
}
}


public class Consumer implements Runnable {
private QueueBuffer q;
Consumer(QueueBuffer q) {
this.q = q;
new Thread(this, "Consumer").start();
}
public void run() {
while (true) {
q.get();
}
}
}


public class Main {
public static void main(String[] args) {
QueueBuffer q = new QueueBuffer();
new Producer(q);
new Consumer(q);
System.out.println("Press Control-C to stop.");
}
}

所以,JVM通过在执行的时候抛出IllegalMonitorStateException的异常,来确保wait, notify时,获得了对象的锁,从而消除隐藏的Race Condition。
最后来看看一道题:写一个多线程程序,交替输出1,2,1,2,1,2......
利用wait, notify解决:

1 public class OutputThread implements Runnable {
2
3 private int num;
4 private Object lock;
5
6 public OutputThread(int num, Object lock) {
7 super();
8 this.num = num;
9 this.lock = lock;
10 }
11
12 public void run() {
13 try {
14 while(true){
15 synchronized(lock){
16 lock.notifyAll();
17 lock.wait();
18 System.out.println(num);
19 }
20 }
21 } catch (InterruptedException e) {
22 // TODO Auto-generated catch block
23 e.printStackTrace();
24 }
25
26 }
27
28 public static void main(String[] args){
29 final Object lock = new Object();
30
31 Thread thread1 = new Thread(new OutputThread(1,lock));
32 Thread thread2 = new Thread(new OutputThread(2, lock));
33
34 thread1.start();
35 thread2.start();
36 }
37
38 }

《Java Concurrency in Practice》里的第14章,对wait, notify有更加详细的介绍。
参考:
http://javarevisited.blogspot.hk/2011/05/wait-notify-and-notifyall-in-java.html
JAVA线程同步 (一)wait(), notify()和notifyAll()使用的更多相关文章
- java 线程之间通信以及notify与notifyAll区别。
jvm多个线程间的通信是通过 线程的锁.条件语句.以及wait().notify()/notifyAll组成. 下面来实现一个启用多个线程来循环的输出两个不同的语句. package com.app. ...
- 如何在 Java 中正确使用 wait, notify 和 notifyAll(转)
wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait ...
- java 线程同步 原理 sleep和wait区别
java线程同步的原理java会为每个Object对象分配一个monitor, 当某个对象(实例)的同步方法(synchronized methods)被多个线程调用时,该对象的monitor将负责处 ...
- 如何在 Java 中正确使用 wait, notify 和 notifyAll – 以生产者消费者模型为例
wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait ...
- java线程同步问题——由腾讯笔试题引发的风波
刚刚wm问我了一道线程的问题,因为自己一直是coder界里的渣渣.所以就须要恶补一下. 2016年4月2号题目例如以下. import java.util.logging.Handler; /** * ...
- JAVA - 线程同步和线程调度的相关方法
JAVA - 线程同步和线程调度的相关方法 wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁:wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等 ...
- Java线程同步_1
Java线程同步_1 synchronized 该同步机制的的核心是同步监视器,任何对象都可以作为同步监视器,代码执行结束,或者程序调用了同步监视器的wait方法会导致释放同步监视器 synchron ...
- Java线程同步之一--AQS
Java线程同步之一--AQS 线程同步是指两个并发执行的线程在同一时间不同时执行某一部分的程序.同步问题在生活中也很常见,就比如在麦当劳点餐,假设只有一个服务员能够提供点餐服务.每个服务员在同一时刻 ...
- java线程 同步临界区:thinking in java4 21.3.5
java线程 同步临界区:thinking in java4 21.3.5 thinking in java 4免费下载:http://download.csdn.net/detail/liangru ...
- Java线程同步的四种方式详解(建议收藏)
Java线程同步属于Java多线程与并发编程的核心点,需要重点掌握,下面我就来详解Java线程同步的4种主要的实现方式@mikechen 目录 什么是线程同步 线程同步的几种方式 1.使用sync ...
随机推荐
- Java程序的编译过程?由.java 到.class的过程?
Javac是一种编译器,它的任务就是将Java源代码语言转化为JVM能够识别的一种语言,然后由JVM将JVM语言再转化成当前这个机器能够识别的机器语言 词法分析器:读取源代码,一个字节一个自己的读取出 ...
- Java成长之路
怎样学习才能从一名Java初级程序员成长为一名合格的架构师,或者说一名合格的架构师应该有怎样的技术知识体系,这是不仅一个刚刚踏入职场的初级程序员也是工作三五年之后开始迷茫的老程序员经常会问到的问题.希 ...
- 简化LINUX的命令输入 简化linux命令 快捷键 短路径
在LINUX中,有很多常用的命令,常用的命令我们可以熟练的记忆,但是对于不经常使用的命令恐怕是需要翻阅手册了,但是我们可以简化这些命令的输入来达到简便记忆的效果. 这里以BSH为例: 编辑/etc/b ...
- Ubuntu 16.04安装Jetty Web服务器
一.下载 http://www.eclipse.org/jetty/download.html 二.安装 tar -zxvf jetty-distribution-9.4.7.v20170914.ta ...
- Jquery表单序列化和json操作
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 使用 Git + Dropbox + SourceTree 做 Source Code Management
此篇文章主要針對有安裝 XCode 的 Mac 用戶. Git 版本控管工具,作用類似 CVS.Subversion(簡 稱SVN),好處在於 Git 不像 CVS 及 SVN 是屬於集中式的版本控管 ...
- 【hibernate】报错:org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not execute statement
报错如下: org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a ...
- 第24章、OnLongClickListener长按事件(从零开始学Android)
在Android App应用中,OnLongClick事件表示长按2秒以上触发的事件,本章我们通过长按图像设置为墙纸来理解其具体用法. 知识点:OnLongClickListener OnLongCl ...
- The most widely used name server software: BIND
https://www.isc.org/downloads/bind/ The most widely used name server software: BIND BIND is open sou ...
- OpenCV中使用SVM简介
下面这是opencv官方文档中的代码,我加了一部分注释: #include "stdafx.h" #include "opencv2/core/core.hpp" ...