java wait() notify_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) throwsInterruptedException {

Object obj = newObject();

obj.wait();

obj.notifyAll();

}

执行以上代码,会抛出java.lang.IllegalMonitorStateException的异常。

2. 执行wait, notify时,不获得该对象的锁会如何?

请看代码:

48304ba5e6f9fe08f3fa1abda7d326ab.png

public static void main(String[] args) throwsInterruptedException {

Object obj = newObject();

Object lock = newObject();

synchronized(lock) {

obj.wait();

obj.notifyAll();

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

执行代码,同样会抛出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实现的一个生产者、一个消费者和一个单位的缓存的简单模型:

48304ba5e6f9fe08f3fa1abda7d326ab.png

public classQueueBuffer {

intn;

boolean valueSet = false;

synchronized intget() {

if (!valueSet)

try{

wait();

} catch(InterruptedException e) {

System.out.println("InterruptedException caught");

}

System.out.println("Got: " +n);

valueSet = false;

notify();

returnn;

}

synchronized void put(intn) {

if(valueSet)

try{

wait();

} catch(InterruptedException e) {

System.out.println("InterruptedException caught");

}

this.n =n;

valueSet = true;

System.out.println("Put: " +n);

notify();

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

public class Producer implementsRunnable {

privateQueueBuffer q;

Producer(QueueBuffer q) {

this.q =q;

new Thread(this, "Producer").start();

}

public voidrun() {

int i = 0;

while (true) {

q.put(i++);

}

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

public class Consumer implementsRunnable {

privateQueueBuffer q;

Consumer(QueueBuffer q) {

this.q =q;

new Thread(this, "Consumer").start();

}

public voidrun() {

while (true) {

q.get();

}

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

public classMain {

public static voidmain(String[] args) {

QueueBuffer q = newQueueBuffer();

newProducer(q);

newConsumer(q);

System.out.println("Press Control-C to stop.");

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

所以,JVM通过在执行的时候抛出IllegalMonitorStateException的异常,来确保wait, notify时,获得了对象的锁,从而消除隐藏的Race Condition。

最后来看看一道题:写一个多线程程序,交替输出1,2,1,2,1,2......

利用wait, notify解决:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 public class OutputThread implementsRunnable {

2

3 private intnum;

4 privateObject lock;

5

6 public OutputThread(intnum, Object lock) {

7 super();

8 this.num =num;

9 this.lock =lock;

10 }

11

12 public voidrun() {

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 voidmain(String[] args){

29 final Object lock = newObject();

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 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

《Java Concurrency in Practice》里的第14章,对wait, notify有更加详细的介绍。

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值