美文网首页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