美文网首页Java
wait()、notify()、notifyAll()

wait()、notify()、notifyAll()

作者: 刘一一同学 | 来源:发表于2019-07-03 09:11 被阅读0次

1. 概述

wait()、notify()、notifyAll() 适应于生产者和消费者场景,结合synchronized使用。

2. 实现原理

wait()

wait()方法为Object对象的方法,执行该方法之前, 必须先获得锁。执行该方法后,会释放锁,使线程等待,进入阻塞队列。

notify()

notify()默认的唤醒策略是:先进入wait()的线程先被唤醒。

notifyAll()

notifyAll()唤醒所有的等待线程,默认的唤醒策略是:LIFO(后进先出)。

3. 集合WaitSet和EntrySet

JVM会为每一个使用内部锁(synchronized)的对象维护着两个集合:WaitSetEntrySet (抢锁机制)

  • Wait Set:如果线程A调用了wait()方法,那么线程A释放对象锁后,进入到阻塞队列,此时处于Wating状态。(存储被阻塞的线程)
  • Entry Set:如果线程A唤醒后并且持有了对象锁,此时,如果还有其他线程也想获得该对象所的话,它只能进入就绪队列等待CPU调度,并且处于线程的Blocked状态。(存储将要获得锁的线程)

4. 集合中的线程在什么条件下可以转变为Runnable状态?

  • 对于EntrySet中的线程,当对象锁被释放的时候,JVM会唤醒处于Entry Set中的某一个线程,这个线程的状态就从Blocked转变为Runnable状态。
  • 对于WaitSet中的线程,当对象的notify()方法调用时,JVM会唤醒处于WaitSet中的某一个线程,这个线程的状态就从Wating状态转变为Runnable。或者当notifyAll()方法被调用时,WaitSet中的全部线程会转变为Runnable状态,所有WaitSet中被唤醒的线程会被转移到EntrySet中。

5. 为什么wait()方法要在循环中调用?

首先,没有规定说必须要放在while循环,至于放在哪是根据要实现的逻辑决定的。

一般来说,wait()肯定是在触发某个条件时调用的,不是if就是while。放在while里面是防止处于Wating的线程被唤醒后,while里面的条件并没有满足(也有可能当时满足了,但是由于别的线程操作之后,又不满足了),这时就需要再次调用wait()进入阻塞队列,等待下次被唤醒。

6. 为什么wait()、notify()和notifyAll()要与synchronized一起使用?

有synchronized的地方不一定有wait()和notify(),但有wait()和notify()的地方必有synchronized,这是因为wait()和notify()不属于线程类,而是每一个对象都具有的方法。而且,这两个方法都和对象锁有关,有锁的地方,必须有synchronized

7. 为什么wait()、notify()和notifyAll()这些方法不在thread类里面?

简单的说,由于wait()、notify()和notifyAll()都是锁级别的操作,所以把他们定义在Object类中,因为锁属于对象。

8. 使用示例

public class ProducerConsumer {
    public static void main(String args[]) {
        Queue<Integer> queue = new LinkedList<>();
        Thread producer = new Producer(queue);
        Thread consumerA = new ConsumerA(queue);
        Thread consumerB = new ConsumerB(queue);
        producer.start();
        consumerA.start();
        consumerB.start();
    }
}

/**
 * Producer
 */
class Producer extends Thread {
    private Queue<Integer> queue;
    public Producer(Queue<Integer> queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (queue) {
                while (!queue.isEmpty()) {
                    try {
                        queue.wait();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
                Random random = new Random();
                int i = random.nextInt();
                System.out.println("生产数据: " + i);
                queue.add(i);
                // queue.notify();
                queue.notifyAll();
            }
        }
    }
}

/**
 * Consumer
 */
class ConsumerA extends Thread {
    private Queue<Integer> queue;
    public ConsumerA(Queue<Integer> queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (queue) {
                while (queue.isEmpty()) {
                    try {
                        queue.wait();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
                System.out.println("消费数据A: " + queue.remove());
                queue.notifyAll();
            }
        }
    }
}

class ConsumerB extends Thread {
    private Queue<Integer> queue;
    public ConsumerB(Queue<Integer> queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (queue) {
                while (queue.isEmpty()) {
                    try {
                        queue.wait();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
                System.out.println("消费数据B: " + queue.remove());
                queue.notifyAll();
            }
        }
    }

}

相关文章

网友评论

    本文标题:wait()、notify()、notifyAll()

    本文链接:https://www.haomeiwen.com/subject/xnhzcctx.html