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

http://www.ticmy.com/?p=219

Java的wait(), notify()和notifyAll()使用小结的更多相关文章

  1. Java Thread wait, notify and notifyAll Example

    Java Thread wait, notify and notifyAll Example Java线程中的使用的wait,notify和nitifyAll方法示例. The Object clas ...

  2. [译]Java Thread wait, notify和notifyAll示例

    Java Thread wait, notify和notifyAll示例 Java上的Object类定义了三个final方法用于不同线程间关于某资源上的锁状态交互,这三个方法是:wait(), not ...

  3. java wait()和notify()、notifyAll()

    图见<JAVA并发编程的艺术>P98-101 这三个方法都是java.lang.Object的方法,用于协调多个线程对共享数据的存取,必须在synchronized语句块中使用!这三个方法 ...

  4. Java多线程 wait, notify 和 notifyAll

    Java的Object类 public class Object { public final native void notify(); public final native void notif ...

  5. Java的wait(), notify()和notifyAll()使用心得(转)

    本篇文章是对java的 wait(),notify(),notifyAll()进行了详细的分析介绍,需要的朋友参考下wait(),notify()和notifyAll()都是java.lang.Obj ...

  6. java中的notify和notifyAll有什么区别?

    先说两个概念:锁池和等待池 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入 ...

  7. 精通java并发-wait,notify和notifyAll的总结(含案例)

    目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages wait,notify和notifyAll 总结 在调用wait方法时,线程必须要持有被调用对象的锁,当调用wait方 ...

  8. Java 中 wait, notify 和 notifyAll的正确使用 – 以生产者消费者模型为例

    如何使用Wait 尽管关于wait和notify的概念很基础,它们也都是Object类的函数,但用它们来写代码却并不简单.如果你在面试中让应聘者来手写代码,用wait和notify解决生产者消费者问题 ...

  9. Java 多线程学习笔记:wait、notify、notifyAll的阻塞和恢复

    前言:昨天尝试用Java自行实现生产者消费者问题(Producer-Consumer Problem),在coding时,使用到了Condition的await和signalAll方法,然后顺便想起了 ...

随机推荐

  1. C#设计模式(17)——观察者模式(Observer Pattern)

    一.引言 在现实生活中,处处可见观察者模式,例如,微信中的订阅号,订阅博客和QQ微博中关注好友,这些都属于观察者模式的应用.在这一章将分享我对观察者模式的理解,废话不多说了,直接进入今天的主题. 二. ...

  2. T4 模板 : 一种提升ASP.NET MVC开发速度方法

    最近由于需要在框架中提供一些自定义模板的功能,找到了一篇博客,可惜似乎是翻译工具直接翻的,读不通顺,就试着自己翻译下,我不会完全翻译原文的句子,可能会对原文进行小范围的我认为更合适的句子并添加些注释, ...

  3. 提高tomcat的并发能力

    1.Apache + Tomcat 结合起来用Apache负责静态页面,Tomcat负责动态页面,同时减少connectionTimeout的时间,以应对并发量大线程回收来不及的情况. 2.压力过大的 ...

  4. Atitit.软件兼容性原理与实践 v3 q326.docx

    Atitit.软件兼容性原理与实践 v3 q326.docx 1. 架构兼容性1 2. Api兼容性1 2.1. 新api  vs  修改旧的api1 3. Web方面的兼容性(js,html)1 3 ...

  5. paip.提升性能---并行多核编程哈的数据结构list,set,map

    paip.提升性能---并行多核编程哈的数据结构list,set,map vector/copyonwritearraylist 都是线程安全的. 或者经过包装的list ::: collection ...

  6. atitit. 解决org.hibernate.SessionException Session is closed

    atitit. 解决org.hibernate.SessionException Session is closed   #--现象:: org.hibernate.SessionException ...

  7. Android Studio 生成Release版,报Warning的解决办法

    转载请注明出处:http://www.cnblogs.com/cnwutianhao/p/6242227.html 请尊重知识产权!!! 同步更新到CSDN:http://blog.csdn.net/ ...

  8. 更新日志 - fir.im 回归,上线 Android Studio 插件

    上周 fir.im 经历了一场前所未有的挑战,因为自查应用网站暂停,在事情发生4天内我们完成了自查,fir.im 正式回归.煎熬的 98 个小时,感谢开发者与用户对我们的信任和支持. 使用注意: 禁止 ...

  9. css核心基础总结篇

    今日这篇是整合前面的css补充知识的. 我觉得前面的关于css的知识补充进去有点乱,今日整理整理一下. 层叠样式表 层叠是什么意思?为什么这个词如此重要,以至于要出现在它的名称里. 层叠可以简单地理解 ...

  10. android: SQLite添加数据

    现在你已经掌握了创建和升级数据库的方法,接下来就该学习一下如何对表中的数据进 行操作了.其实我们可以对数据进行的操作也就无非四种,即 CRUD.其中 C 代表添加 (Create),R 代表查询(Re ...