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

2016-02-19 08:57 12 1 收藏

今天图老师小编要向大家分享个Java的wait(), notify()和notifyAll()使用心得教程,过程简单易学,相信聪明的你一定能轻松get!

【 tulaoshi.com - 编程语言 】

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解决:
代码如下:

public class OutputThread implements Runnable {
    private int num;
    private Object lock;
    public OutputThread(int num, Object lock) {
        super();
        this.num = num;
        this.lock = lock;
    }
    public void run() {
        try {
            while(true){
                synchronized(lock){
                    lock.notifyAll();
                    lock.wait();
                    System.out.println(num);
                }
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public static void main(String[] args){
        final Object lock = new Object();
        Thread thread1 = new Thread(new OutputThread(1,lock));
        Thread thread2 = new Thread(new OutputThread(2, lock));
        thread1.start();
        thread2.start();
    }
}

来源:http://www.tulaoshi.com/n/20160219/1588987.html

延伸阅读
标签: 办公软件
“邮件合并”是Word的一项高级功能,是办公自动化人员应该掌握的基本技术之一。但是大多数书上对“邮件合并”的介绍都很简单,如果按照书的上介绍去做,合并打印出的邮件并不能完全令人满意。教你几个邮件合并技巧,希望能帮你提高办公效率。 1.用一页纸打印多个邮件 利用Word“邮件合并”可以批量处理和打印邮件,很多情况下我们的...
标签: 韩后 补水 护肤
韩后花痴水漾怎么使用 1、净:花痴水漾保湿洁颜啫喱100g氨基酸配方,清爽洁净,不紧绷。玫瑰花瓣的果冻质地,棉花糖般绵密泡沫。 2、调:花痴水漾保湿美容液100ml清爽补水,滋养快吸收,浓醇的精华质地。 3、养:花痴水漾保湿凝露100g外保湿,内储水,清爽易吸收。一抹沁肤的凝露。 4、护:花痴水漾保湿睡眠面膜100g锁住水分,...
标签: PS PS教程
  文/刘明 Photoshop作为一个专业级的图像处理软件,其功能强大自不必多说。在使用过程中笔者摸索出一些书本上没有的小技巧,不敢独享,在此与大家共同学习。 1、去掉扫描图片中的龟纹。用过扫描仪的朋友都知道,用扫描方式输入的图片会有一些龟纹,如果我们不管它而在此基础上进行编辑处理,会影响以后的视觉和整体效果。...
标签: 电脑入门
一、人性化设计--最能打动人 使用过的查询、解释中不懂得字词,都很好用! (1)怎样看已经查过的词? 要查找以前查询过的单词或文字,可以在输入框的下拉列表里找到。 (2)解释中的字词也可以查吗? 当一个词的解释显示在详细解释窗口中后,用鼠标点击击的内容中想查的字或词,同样可以得到它的解释。 (3)生词本怎么用? 用户查过的所有生词都...
标签: 黑暗之魂
《黑暗之魂》力量和敏捷武器使用心得 黑暗之魂是款以难度著称的游戏,由于这款游戏对操作的要求非常高,所以新手在前期被虐是非常正常的事情,或者即使到后期也有可能被boss所虐,所以选择正确的武器对玩家来说非常重要。 力量和敏捷武器使用心得 黑暗之魂一直以其丰富的玩法,以及优秀的角色扮演成分,吸引着无数玩家。而因此,也一直有...

经验教程

102

收藏

51
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部