Java的wait(), notify()和notifyAll()使用小结

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的锁。**这是因为等待状态条件的机制和保证状态连续性的机制是紧密的结合在一起的。

**
根据上述两点,在调用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的异常。

  1. 执行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的异常。

  1. 为什么在执行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有更加详细的介绍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懒羊羊^O^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值