如何在 Java 中正确使用 wait,notify 和 notifyAll

如题所述

我们先来了解一下为什么要使用wait,notify


首先看一下以下代码:

synchronized(a){
        ...//1
synchronized(b){
}
}
synchronized(b){
... //2
synchronized(a){
}
}

假设现在有两个线程, t1 线程运行到了//1 的位置,而 t2 线程运行到了//2 的位置,接

下来会发生什么情况呢?

此时, a 对象的锁标记被 t1 线程获得,而 b 对象的锁标记被 t2 线程获得。对于 t1 线程

而言,为了进入对 b 加锁的同步代码块, t1 线程必须获得 b 对象的锁标记。由于 b 对象的锁标记被 t2 线程获得, t1 线程无法获得这个对象的锁标记,因此它会进入 b 对象的锁池,等待 b 对象锁标记的释放。而对于 t2 线程而言,由于要进入对 a 加锁的同步代码块,由于 a 对象的锁标记在 t1 线程手中,因此 t2 线程会进入 a 对象的锁池。

此时, t1 线程在等待 b 对象锁标记的释放,而 t2 线程在等待 a 对象锁标记的释放。由

于两边都无法获得所需的锁标记,因此两个线程都无法运行。这就是“死锁”问题。


在 Java 中,采用了 wait 和 notify 这两个方法,来解决死锁机制。

首先,在 Java 中,每一个对象都有两个方法: wait 和 notify 方法。这两个方法是定义

在 Object 类中的方法。对某个对象调用 wait()方法,表明让线程暂时释放该对象的锁标记。

例如,上面的代码就可以改成:

synchronized(a){
...//1
a.wait();
synchronized(b){
}
}
synchronized(b){
... //2
synchronized(a){
...
a.notify();
}
}

这样的代码改完之后,在//1 后面, t1 线程就会调用 a 对象的 wait 方法。此时, t1 线程

会暂时释放自己拥有的 a 对象的锁标记,而进入另外一个状态:等待状态。

要注意的是,如果要调用一个对象的 wait 方法,前提是线程已经获得这个对象的锁标

记。如果在没有获得对象锁标记的情况下调用 wait 方法,则会产生异常。

由于 a 对象的锁标记被释放,因此, t2 对象可以获得 a 对象的锁标记,从而进入对 a

加锁的同步代码块。在同步代码块的最后,调用 a.notify()方法。这个方法与 wait 方法相对应,是让一个线程从等待状态被唤醒。

那么 t2 线程唤醒 t1 线程之后, t1 线程处于什么状态呢?由于 t1 线程唤醒之后还要在

对 a 加锁的同步代码块中运行,而 t2 线程调用了 notify()方法之后,并没有立刻退出对 a 锁的同步代码块,因此此时 t1 线程并不能马上获得 a 对象的锁标记。因此,此时, t1 线程会在 a 对象的锁池中进行等待,以期待获得 a 对象的锁标记。也就是说,一个线程如果之前调用了 wait 方法,则必须要被另一个线程调用 notify()方法唤醒。唤醒之后,会进入锁池状态。线程状态转换图如下:

由于可能有多个线程先后调用 a 对象 wait 方法,因此在 a 对象等待状态中的线程可能

有多个。而调用 a.notify()方法,会从 a 对象等待状态中的多个线程里挑选一个线程进行唤醒。

与之对应的,有一个 notifyAll()方法, 调用 a.notifyAll() 会把 a 对象等待状态中的所有线程都唤醒。


下面结合一个实际的例子,来演示如何使用 wait 和 notify / notifyAll 方法。

我使用一个数组来模拟一个比较熟悉的数据结构:栈。代码如下所示:

class MyStack{
    private char[] data = new char[5];
    private int index = 0;
    public char pop(){
        index -- ;
        return data[index];
    }
    public void push(char ch){
        data[index] = ch;
        index++;
    }
    public void print(){
        for (int i=0; i<index; i++){
            System.out.print(data[i] + "\t");
        }
        System.out.println();
    }
    public boolean isEmpty(){
        return index == 0;
    }
    public boolean isFull(){
        return index == 5;
    }
}

注意,我们为 MyStack 增加了两个方法,一个用来判断栈是否为空,一个用来判断栈是

否已经满了。

然后我们创建两个线程,一个线程每隔一段随机的时间,就会往栈中增加一个数据;另

一个线程每隔一段随机的时间,就会从栈中取走一个数据。为了保证 push 和 pop 的完整性,在线程中应当对 MyStack 对象加锁。

但是我们发现,入栈线程和出栈线程并不是在任何时候都可以工作的。当数组满了的时

候,入栈线程将不能工作;当数组空了的时候,出栈线程也不能工作。违反了上面的条件,我们将得到一个数组下标越界异常。

为此,我们可以用 wait/notify 机制。在入栈线程执行入栈操作时,如果发现数组已满,

则会调用 wait 方法,去等待。同样,出栈线程在执行出栈操作时,如果发现数组已空,同

样调用 wait 方法去等待。在入栈线程结束入栈工作之后,会调用 notifyAll 方法,释放那些正在等待的出栈线程(因为数组现在已经不是空的了,他们可以恢复工作了)。同样,当出栈线程结束出栈工作之后,也会调用 notifyAll 方法,释放正在等待的入栈线程。


一段生产者/消费者相关代码:

class Consumer extends Thread{
    private MyStack ms;
    public Consumer(MyStack ms) {
        this.ms = ms;
    }
    public void run(){
        while(true){
        //为了保证 push 和 pop 操作的完整性
        //必须加 synchronized
            synchronized(ms){
            //如果栈空间已满,则 wait()释放 ms 的锁标记
                while(ms.isEmpty()){
                    try {
                        ms.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                char ch = ms.pop();
                System.out.println("Pop " + ch);
                ms.notifyAll();
            }
            //push 之后随机休眠一段时间
            try {
                sleep( (int)Math.abs(Math.random() * 100) );
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//生产者
class Producer extends Thread{
    private MyStack ms;
    public Producer(MyStack ms) {
        this.ms = ms;
    }
    public void run(){
        while(true){
        //为了保证 push 和 pop 操作的完整性
        //必须加 synchronized
            synchronized(ms){
            //如果栈空间已满,则 wait()释放 ms 的锁标记
                while(ms.isFull()){
                    try {
                        ms.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                ms.push('A');
                System.out.println("push A");
                ms.notifyAll();
            }
            //push 之后随机休眠一段时间
            try {
                sleep( (int)Math.abs(Math.random() * 200) );
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//主方法中,启用生产者与消费者两个线程
public class TestWaitNotify {
    public static void main(String[] args) {
        MyStack ms = new MyStack();
        Thread t1 = new Producer(ms);
        Thread t2 = new Consumer(ms);
        t1.start();
        t2.start();
    }
}

部分代码纯手打,望采纳~

温馨提示:答案为网友推荐,仅供参考
第1个回答  2016-03-21
wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视。本文对这些关键字的使用进行了描述。

在 Java 中可以用 wait、notify 和 notifyAll
来实现线程间的通信。。举个例子,如果你的Java程序中有两个线程——即生产者和消费者,那么生产者可以通知消费者,让消费者开始消耗数据,因为队列缓
冲区中有内容待消费(不为空)。相应的,消费者可以通知生产者可以开始生成更多的数据,因为当它消耗掉某些数据后缓冲区不再为满。

我们可以利用wait()来让一个线程在某些条件下暂停运行。例如,在生产者消费者模型中,生产者线程在缓冲区为满的时候,消费者在缓冲区为空的时
候,都应该暂停运行。如果某些线程在等待某些条件触发,那当那些条件为真时,你可以用 notify 和 notifyAll
来通知那些等待中的线程重新开始运行。不同之处在于,notify 仅仅通知一个线程,并且我们不知道哪个线程会收到通知,然而 notifyAll
会通知所有等待中的线程。换言之,如果只有一个线程在等待一个信号灯,notify和notifyAll都会通知到这个线程。但如果多个线程在等待这个信
号灯,那么notify只会通知到其中一个,而其它线程并不会收到任何通知,而notifyAll会唤醒所有等待中的线程。

在这篇文章中你将会学到如何使用 wait、notify 和 notifyAll 来实现线程间的通信,从而解决生产者消费者问题。如果你想要更深入地学习Java中的多线程同步问题,我强烈推荐阅读Brian Goetz所著的《Java Concurrency in Practice | Java 并发实践》,不读这本书你的 Java 多线程征程就不完整哦!这是我最向Java开发者推荐的书之一。

如何使用Wait

尽管关于wait和notify的概念很基础,它们也都是Object类的函数,但用它们来写代码却并不简单。如果你在面试中让应聘者来手写代码,
用wait和notify解决生产者消费者问题,我几乎可以肯定他们中的大多数都会无所适从或者犯下一些错误,例如在错误的地方使用
synchronized 关键词,没有对正确的对象使用wait,或者没有遵循规范的代码方法。说实话,这个问题对于不常使用它们的程序员来说确实令人感觉比较头疼。

第一个问题就是,我们怎么在代码里使用wait()呢?因为wait()并不是Thread类下的函数,我们并不能使用
Thread.call()。事实上很多Java程序员都喜欢这么写,因为它们习惯了使用Thread.sleep(),所以他们会试图使用wait()

来达成相同的目的,但很快他们就会发现这并不能顺利解决问题。正确的方法是对在多线程间共享的那个Object来使用wait。在生产者消费者问题中,这
个共享的Object就是那个缓冲区队列。

第二个问题是,既然我们应该在synchronized的函数或是对象里调用wait,那哪个对象应该被synchronized呢?答案是,那个

你希望上锁的对象就应该被synchronized,即那个在多个线程间被共享的对象。在生产者消费者问题中,应该被synchronized的就是那个
缓冲区队列。(我觉得这里是英文原文有问题……本来那个句末就不应该是问号不然不太通……)

永远在循环(loop)里调用 wait 和 notify,不是在 If 语句

现在你知道wait应该永远在被synchronized的背景下和那个被多线程共享的对象上调用,下一个一定要记住的问题就是,你应该永远在
while循环,而不是if语句中调用wait。因为线程是在某些条件下等待的——在我们的例子里,即“如果缓冲区队列是满的话,那么生产者线程应该等
待”,你可能直觉就会写一个if语句。但if语句存在一些微妙的小问题,导致即使条件没被满足,你的线程你也有可能被错误地唤醒。所以如果你不在线程被唤

醒后再次使用while循环检查唤醒条件是否被满足,你的程序就有可能会出错——例如在缓冲区为满的时候生产者继续生成数据,或者缓冲区为空的时候消费者
开始小号数据。所以记住,永远在while循环而不是if语句中使用wait!我会推荐阅读《Effective Java》,这是关于如何正确使用wait和notify的最好的参考资料。

基于以上认知,下面这个是使用wait和notify函数的规范代码模板:
// The standard idiom for calling the wait method in Java synchronized (sharedObject) { while (condition) { sharedObject.wait(); // (Releases lock, and reacquires on wakeup) } // do action based upon condition e.g. take or put into queue }

就像我之前说的一样,在while循环里使用wait的目的,是在线程被唤醒的前后都持续检查条件是否被满足。如果条件并未改变,wait被调用之前notify的唤醒通知就来了,那么这个线程并不能保证被唤醒,有可能会导致死锁问题。

Java wait(), notify(), notifyAll() 范例

下面我们提供一个使用wait和notify的范例程序。在这个程序里,我们使用了上文所述的一些代码规范。我们有两个线程,分别名为
PRODUCER(生产者)和CONSUMER(消费者),他们分别继承了了Producer和Consumer类,而Producer和
Consumer都继承了Thread类。Producer和Consumer想要实现的代码逻辑都在run()函数内。Main线程开始了生产者和消费
者线程,并声明了一个LinkedList作为缓冲区队列(在Java中,LinkedList实现了队列的接口)。生产者在无限循环中持续往
LinkedList里插入随机整数直到LinkedList满。我们在while(queue.size ==
maxSize)循环语句中检查这个条件。请注意到我们在做这个检查条件之前已经在队列对象上使用了synchronized关键词,因而其它线程不能在
我们检查条件时改变这个队列。如果队列满了,那么PRODUCER线程会在CONSUMER线程消耗掉队列里的任意一个整数,并用notify来通知
PRODUCER线程之前持续等待。在我们的例子中,wait和notify都是使用在同一个共享对象上的。
import java.util.LinkedList; import java.util.Queue; import java.util.Random; /** * Simple Java program to demonstrate How to use wait, notify and notifyAll() * method in Java by solving producer consumer problem. * * @author Javin Paul */ public class ProducerConsumerInJava { public static void main(String args[]) { System.out.println("How to use wait and notify method in Java"); System.out.println("Solving Producer Consumper Problem"); Queue<Integer> buffer = new LinkedList<>(); int maxSize = 10; Thread producer = new Producer(buffer, maxSize, "PRODUCER"); Thread consumer = new Consumer(buffer, maxSize, "CONSUMER"); producer.start(); consumer.start(); } } /** * Producer Thread will keep producing values for Consumer * to consumer. It will use wait() method when Queue is full * and use notify() method to send notification to Consumer * Thread. * * @author WINDOWS 8 * */ class Producer extends Thread { private Queue<Integer> queue; private int maxSize; public Producer(Queue<Integer> queue, int maxSize, String name){ super(name); this.queue = queue; this.maxSize = maxSize; } @Override public void run() { while (true) { synchronized (queue) { while (queue.size() == maxSize) { try { System.out .println("Queue is full, " + "Producer thread waiting for " + "consumer to take something from queue"); queue.wait(); } catch (Exception ex) { ex.printStackTrace(); } } Random random = new Random(); int i = random.nextInt(); System.out.println("Producing value : " + i); queue.add(i); queue.notifyAll(); } } } } /** * Consumer Thread will consumer values form shared queue. * It will also use wait() method to wait if queue is * empty. It will also use notify method to send * notification to producer thread after consuming values * from queue. * * @author WINDOWS 8 * */ class Consumer extends Thread { private Queue<Integer> queue; private int maxSize; public Consumer(Queue<Integer> queue, int maxSize, String name){ super(name); this.queue = queue; this.maxSize = maxSize; } @Override public void run() { while (true) { synchronized (queue) { while (queue.isEmpty()) { System.out.println("Queue is empty," + "Consumer thread is waiting" + " for producer thread to put something in queue"); try { queue.wait(); } catch (Exception ex) { ex.printStackTrace(); } } System.out.println("Consuming value : " + queue.remove()); queue.notifyAll(); } } } }

为了更好地理解这个程序,我建议你在debug模式里跑这个程序。一旦你在debug模式下启动程序,它会停止在PRODUCER或者
CONSUMER线程上,取决于哪个线程占据了CPU。因为两个线程都有wait()的条件,它们一定会停止,然后你就可以跑这个程序然后看发生什么了
(很有可能它就会输出我们以上展示的内容)。你也可以使用Eclipse里的Step into和Step over按钮来更好地理解多线程间发生的事情。

本文重点:

1. 你可以使用wait和notify函数来实现线程间通信。你可以用它们来实现多线程(>3)之间的通信。

2. 永远在synchronized的函数或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。

3. 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。

4. 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。

5. 基于前文提及的理由,更倾向用 notifyAll(),而不是 notify()。

这是关于Java里如何使用wait,
notify和notifyAll的所有重点啦。你应该只在你知道自己要做什么的情况下使用这些函数,不然Java里还有很多其它的用来解决同步问题的方

案。例如,如果你想使用生产者消费者模型的话,你也可以使用BlockingQueue,它会帮你处理所有的线程安全问题和流程控制。如果你想要某一个线

程等待另一个线程做出反馈再继续运行,你也可以使用CycliBarrier或者CountDownLatch。如果你只是想保护某一个资源的话,你也可
以使用Semaphore。本回答被提问者采纳
第2个回答  2016-05-03

wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视。本文对这些关键字的使用进行了描述。

在 Java 中可以用 wait、notify 和 notifyAll 来实现线程间的通信。。举个例子,如果你的Java程序中有两个线程——即生产者和消费者,那么生产者可以通知消费者,让消费者开始消耗数据,因为队列缓 冲区中有内容待消费(不为空)。相应的,消费者可以通知生产者可以开始生成更多的数据,因为当它消耗掉某些数据后缓冲区不再为满。

我们可以利用wait()来让一个线程在某些条件下暂停运行。例如,在生产者消费者模型中,生产者线程在缓冲区为满的时候,消费者在缓冲区为空的时 候,都应该暂停运行。如果某些线程在等待某些条件触发,那当那些条件为真时,你可以用 notify 和 notifyAll 来通知那些等待中的线程重新开始运行。不同之处在于,notify 仅仅通知一个线程,并且我们不知道哪个线程会收到通知,然而 notifyAll 会通知所有等待中的线程。换言之,如果只有一个线程在等待一个信号灯,notify和notifyAll都会通知到这个线程。但如果多个线程在等待这个信 号灯,那么notify只会通知到其中一个,而其它线程并不会收到任何通知,而notifyAll会唤醒所有等待中的线程。

在这篇文章中你将会学到如何使用 wait、notify 和 notifyAll 来实现线程间的通信,从而解决生产者消费者问题。如果你想要更深入地学习Java中的多线程同步问题,我强烈推荐阅读Brian Goetz所著的《Java Concurrency in Practice | Java 并发实践》,不读这本书你的 Java 多线程征程就不完整哦!这是我最向Java开发者推荐的书之一。

如何使用Wait

尽管关于wait和notify的概念很基础,它们也都是Object类的函数,但用它们来写代码却并不简单。如果你在面试中让应聘者来手写代码, 用wait和notify解决生产者消费者问题,我几乎可以肯定他们中的大多数都会无所适从或者犯下一些错误,例如在错误的地方使用 synchronized 关键词,没有对正确的对象使用wait,或者没有遵循规范的代码方法。说实话,这个问题对于不常使用它们的程序员来说确实令人感觉比较头疼。

第一个问题就是,我们怎么在代码里使用wait()呢?因为wait()并不是Thread类下的函数,我们并不能使用 Thread.call()。事实上很多Java程序员都喜欢这么写,因为它们习惯了使用Thread.sleep(),所以他们会试图使用wait() 来达成相同的目的,但很快他们就会发现这并不能顺利解决问题。正确的方法是对在多线程间共享的那个Object来使用wait。在生产者消费者问题中,这 个共享的Object就是那个缓冲区队列。

第二个问题是,既然我们应该在synchronized的函数或是对象里调用wait,那哪个对象应该被synchronized呢?答案是,那个 你希望上锁的对象就应该被synchronized,即那个在多个线程间被共享的对象。在生产者消费者问题中,应该被synchronized的就是那个 缓冲区队列。

永远在循环(loop)里调用 wait 和 notify,不是在 If 语句

现在你知道wait应该永远在被synchronized的背景下和那个被多线程共享的对象上调用,下一个一定要记住的问题就是,你应该永远在 while循环,而不是if语句中调用wait。因为线程是在某些条件下等待的——在我们的例子里,即“如果缓冲区队列是满的话,那么生产者线程应该等 待”,你可能直觉就会写一个if语句。但if语句存在一些微妙的小问题,导致即使条件没被满足,你的线程你也有可能被错误地唤醒。所以如果你不在线程被唤 醒后再次使用while循环检查唤醒条件是否被满足,你的程序就有可能会出错——例如在缓冲区为满的时候生产者继续生成数据,或者缓冲区为空的时候消费者 开始小号数据。所以记住,永远在while循环而不是if语句中使用wait!我会推荐阅读《Effective Java》,这是关于如何正确使用wait和notify的最好的参考资料。

基于以上认知,下面这个是使用wait和notify函数的规范代码模板:


// The standard idiom for calling the wait method in Java synchronized (sharedObject) {     while (condition) { sharedObject.wait();         // (Releases lock, and reacquires on wakeup)     }     // do action based upon condition e.g. take or put into queue } 

就像我之前说的一样,在while循环里使用wait的目的,是在线程被唤醒的前后都持续检查条件是否被满足。如果条件并未改变,wait被调用之前notify的唤醒通知就来了,那么这个线程并不能保证被唤醒,有可能会导致死锁问题。

Java wait(), notify(), notifyAll() 范例

下面我们提供一个使用wait和notify的范例程序。在这个程序里,我们使用了上文所述的一些代码规范。我们有两个线程,分别名为 PRODUCER(生产者)和CONSUMER(消费者),他们分别继承了了Producer和Consumer类,而Producer和 Consumer都继承了Thread类。Producer和Consumer想要实现的代码逻辑都在run()函数内。Main线程开始了生产者和消费 者线程,并声明了一个LinkedList作为缓冲区队列(在Java中,LinkedList实现了队列的接口)。生产者在无限循环中持续往 LinkedList里插入随机整数直到LinkedList满。我们在while(queue.size == maxSize)循环语句中检查这个条件。请注意到我们在做这个检查条件之前已经在队列对象上使用了synchronized关键词,因而其它线程不能在 我们检查条件时改变这个队列。如果队列满了,那么PRODUCER线程会在CONSUMER线程消耗掉队列里的任意一个整数,并用notify来通知 PRODUCER线程之前持续等待。在我们的例子中,wait和notify都是使用在同一个共享对象上的。

import java.util.LinkedList; 
import java.util.Queue; 
import java.util.Random; 
/** 
* Simple Java program to demonstrate How to use wait, notify and notifyAll() 
* method in Java by solving producer consumer problem. 

* @author Javin Paul 
*/ 
public class ProducerConsumerInJava { 
public static void main(String args[]) { 
  System.out.println("How to use wait and notify method in Java"); 
  System.out.println("Solving Producer Consumper Problem"); 
  Queue&lt;Integer&gt; buffer = new LinkedList&lt;&gt;(); 
  int maxSize = 10; 
  Thread producer = new Producer(buffer, maxSize, "PRODUCER"); 
  Thread consumer = new Consumer(buffer, maxSize, "CONSUMER"); 
  producer.start(); consumer.start(); } 

/** 
* Producer Thread will keep producing values for Consumer 
* to consumer. It will use wait() method when Queue is full 
* and use notify() method to send notification to Consumer 
* Thread. 

* @author WINDOWS 8 

*/ 
class Producer extends Thread 
{ private Queue&lt;Integer&gt; queue; 
  private int maxSize; 
  public Producer(Queue&lt;Integer&gt; queue, int maxSize, String name){ 
   super(name); this.queue = queue; this.maxSize = maxSize; 
  } 
  @Override public void run() 
  { 
   while (true) 
    { 
     synchronized (queue) { 
      while (queue.size() == maxSize) { 
       try { 
        System.out .println("Queue is full, " + "Producer thread waiting for " + "consumer to take something from queue"); 
        queue.wait(); 
       } catch (Exception ex) { 
        ex.printStackTrace(); } 
       } 
       Random random = new Random(); 
       int i = random.nextInt(); 
       System.out.println("Producing value : " + i); queue.add(i); queue.notifyAll(); 
      } 
     } 
    } 
   } 
/** 
* Consumer Thread will consumer values form shared queue. 
* It will also use wait() method to wait if queue is 
* empty. It will also use notify method to send 
* notification to producer thread after consuming values 
* from queue. 

* @author WINDOWS 8 

*/ 
class Consumer extends Thread { 
  private Queue&lt;Integer&gt; queue; 
  private int maxSize; 
  public Consumer(Queue&lt;Integer&gt; queue, int maxSize, String name){ 
   super(name); 
   this.queue = queue; 
   this.maxSize = maxSize; 
  } 
  @Override public void run() { 
   while (true) { 
    synchronized (queue) { 
     while (queue.isEmpty()) { 
      System.out.println("Queue is empty," + "Consumer thread is waiting" + " for producer thread to put something in queue"); 
      try { 
       queue.wait(); 
      } catch (Exception ex) { 
       ex.printStackTrace(); 
      } 
     } 
     System.out.println("Consuming value : " + queue.remove()); queue.notifyAll(); 
    } 
   } 
  } 
}

为了更好地理解这个程序,我建议你在debug模式里跑这个程序。一旦你在debug模式下启动程序,它会停止在PRODUCER或者 CONSUMER线程上,取决于哪个线程占据了CPU。因为两个线程都有wait()的条件,它们一定会停止,然后你就可以跑这个程序然后看发生什么了 (很有可能它就会输出我们以上展示的内容)。你也可以使用Eclipse里的Step into和Step over按钮来更好地理解多线程间发生的事情。

本文重点:

1. 你可以使用wait和notify函数来实现线程间通信。你可以用它们来实现多线程(>3)之间的通信。

2. 永远在synchronized的函数或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。

3. 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。

4. 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。

5. 基于前文提及的理由,更倾向用 notifyAll(),而不是 notify()。

第3个回答  推荐于2018-04-20
wait(),notify(),notifyAll()都不属于Thread类,而是属于Object基础类,也就是说每个对像都有wait(),notify(),notifyAll()的功能.因为都个对像都有锁,锁是每个对像的基础,当然操作锁的方法也是最基础了.
  先看java 文档怎么说:
  wait导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。当前的线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行.
  notify唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。此方法只应由作为此对象监视器的所有者的线程来调用.
  "当前的线程必须拥有此对象监视器"与"此方法只应由作为此对象监视器的所有者的线程来调用"说明wait方法与notify方法必须在同步块内执行,即synchronized(obj之内).
  调用对像wait方法后,当前线程释放对像锁,进入等待状态.直到其他线程(也只能是其他线程)通过notify 方法,或 notifyAll.该线程重新获得对像锁.
  继续执行,记得线程必须重新获得对像锁才能继续执行.因为synchronized代码块内没有锁是寸步不能走的本回答被网友采纳
第4个回答  2016-08-12
wait,notify 和 notifyAll 只能用在包含 synchronized 的方法 ,或 同步代码块中
个人认为wait notify 和 notifyAll 不是保留关键字, 因为是 Object 的方法. 所以每一个类都有.

wait ()使线程进入--->等待队列,当前线程会抛弃所持有的锁.

当使用 notify 或 notifyAll 可以将当前的线程从
等待队列 进入到 锁池

相关了解……

你可能感兴趣的内容

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 非常风气网