Java多线程:wait、notify(notifyAll)线程唤醒应用

前提概要

在Java的Object类中有三个final的方法允许线程之间进行资源对象锁的通信,他们分别是: wait(), notify() and notifyAll()。

调用这些方法的当前线程必须拥有此对象监视器,否则将会报java.lang.IllegalMonitorStateException exception异常。

  • wait
    Object的wait方法有三个重载方法,其中一个方法wait() 是无限期(一直)等待,直到其它线程调用notify或notifyAll方法唤醒当前的线程;另外两个方法wait(long timeout) 和wait(long timeout, int nanos)允许传入 当前线程在被唤醒之前需要等待的时间,timeout为毫秒数,nanos为纳秒数。

  • notify
    notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。

  • notifyAll
    notifyAll 会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。

这些方法可以使用于“生产者-消费者”问题,消费者是在队列中等待对象的线程,生产者是在队列中释放对象并通知其他线程的线程。

在线程唤醒机制中如果处理不当会出现虚假唤醒(spurious wakeup)的情况,也就是说线程在等待信号时即使条件不满足,但是线程仍然可能被唤醒,解决这种问题的办法是在条件判断时使用while而不使用if,例如:

while (num == 0) {
      System.out.println("现有鸡蛋:" + num + "颗,消费者不可消费!");
      this.wait();
}

让我们来看一个多线程作用于同一个对象的例子,我们使用wait, notify and notifyAll方法。


实现代码

package com.oumuv.producerandconsumer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 生产者与消费者问题:wait、notify(notifyAll)应用
 * 一个篮子可存20颗鸡蛋,鸡蛋数量小于5时生产者开始生产鸡蛋,篮子满后生产者停止生产并进入等待状态,鸡蛋数量等于0时消费者不能消费
 *
 */
public class ProducerAndConsumer2 {

    public static void main(String[] args) {
        ProducerAndConsumer2 pac = new ProducerAndConsumer2();

        EggKep eggKep = pac.new EggKep();
        Producer p1 = pac.new Producer(eggKep);
        Consumer c1 = pac.new Consumer(eggKep);

        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.submit(p1);
        executorService.submit(c1);
    }


    /**
     * 生产者
     */
    class Producer implements Runnable {
        EggKep agg;
        public Producer(EggKep agg) {
            this.agg = agg;
        }
        @Override
        public void run() {
            try {
                for (int i = 0; i < 50; i++) {
                    agg.produce();
                    Thread.sleep(300);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 消费者
     */
    class Consumer implements Runnable {
        EggKep agg;
        public Consumer(EggKep agg) {
            this.agg = agg;
        }
        @Override
        public void run() {
            try {
                for (int i = 0; i < 50; i++) {
                    agg.consume();
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 鸡蛋篮子
     */
    class EggKep {
        int maxSize = 20;//篮子大小
        int num = 0;//鸡蛋数量
        boolean flag = true;//是否开始生产,默认true生产

        public EggKep(int maxSize, int num) {
            this.maxSize = maxSize;
            this.num = num;
        }

        public EggKep() {
        }
		/**
		*生产方法
		*/
        protected synchronized void produce() throws InterruptedException {
        	//为了避免虚假唤醒,需要不断的检测满足条件
            while (!flag) {
                this.wait();
            }
            System.out.println("生产了一颗鸡蛋,现有鸡蛋:" + (++num) + "颗");
            if (num == maxSize) {
                flag = false;
                System.out.println("篮子满了,生产者停止工作!");
            }
            this.notify();
        }
		/**
		*消费方法
		*/
        protected synchronized void consume() throws InterruptedException {
            while (num == 0) {
                System.out.println("现有鸡蛋:" + num + "颗,消费者不可消费!");
                this.wait();
            }
            System.out.println("消费一颗鸡蛋,现有鸡蛋:" + (--num) + "颗");
            if (num <= 5 && !flag) {
                flag = true;
                System.out.println("通知生产者开始工作!");
            }
            this.notify();
        }
    }
}


可能输出的结果

现有鸡蛋:0颗,消费者不可消费!
生产了一颗鸡蛋,现有鸡蛋:1颗
消费一颗鸡蛋,现有鸡蛋:0颗
生产了一颗鸡蛋,现有鸡蛋:1颗
生产了一颗鸡蛋,现有鸡蛋:2颗
生产了一颗鸡蛋,现有鸡蛋:3颗
消费一颗鸡蛋,现有鸡蛋:2颗
生产了一颗鸡蛋,现有鸡蛋:3颗
生产了一颗鸡蛋,现有鸡蛋:4颗
生产了一颗鸡蛋,现有鸡蛋:5颗
消费一颗鸡蛋,现有鸡蛋:4颗
生产了一颗鸡蛋,现有鸡蛋:5颗
生产了一颗鸡蛋,现有鸡蛋:6颗
生产了一颗鸡蛋,现有鸡蛋:7颗
消费一颗鸡蛋,现有鸡蛋:6颗
生产了一颗鸡蛋,现有鸡蛋:7颗
生产了一颗鸡蛋,现有鸡蛋:8颗
生产了一颗鸡蛋,现有鸡蛋:9颗
生产了一颗鸡蛋,现有鸡蛋:10颗
消费一颗鸡蛋,现有鸡蛋:9颗
生产了一颗鸡蛋,现有鸡蛋:10颗
生产了一颗鸡蛋,现有鸡蛋:11颗
生产了一颗鸡蛋,现有鸡蛋:12颗
消费一颗鸡蛋,现有鸡蛋:11颗
生产了一颗鸡蛋,现有鸡蛋:12颗
生产了一颗鸡蛋,现有鸡蛋:13颗
生产了一颗鸡蛋,现有鸡蛋:14颗
消费一颗鸡蛋,现有鸡蛋:13颗
生产了一颗鸡蛋,现有鸡蛋:14颗
生产了一颗鸡蛋,现有鸡蛋:15颗
生产了一颗鸡蛋,现有鸡蛋:16颗
生产了一颗鸡蛋,现有鸡蛋:17颗
消费一颗鸡蛋,现有鸡蛋:16颗
生产了一颗鸡蛋,现有鸡蛋:17颗
生产了一颗鸡蛋,现有鸡蛋:18颗
生产了一颗鸡蛋,现有鸡蛋:19颗
消费一颗鸡蛋,现有鸡蛋:18颗
生产了一颗鸡蛋,现有鸡蛋:19颗
生产了一颗鸡蛋,现有鸡蛋:20颗
篮子满了,生产者停止工作!
消费一颗鸡蛋,现有鸡蛋:19颗
消费一颗鸡蛋,现有鸡蛋:18颗
消费一颗鸡蛋,现有鸡蛋:17颗
消费一颗鸡蛋,现有鸡蛋:16颗
消费一颗鸡蛋,现有鸡蛋:15颗
消费一颗鸡蛋,现有鸡蛋:14颗
消费一颗鸡蛋,现有鸡蛋:13颗
消费一颗鸡蛋,现有鸡蛋:12颗
消费一颗鸡蛋,现有鸡蛋:11颗
消费一颗鸡蛋,现有鸡蛋:10颗
消费一颗鸡蛋,现有鸡蛋:9颗
消费一颗鸡蛋,现有鸡蛋:8颗
消费一颗鸡蛋,现有鸡蛋:7颗
消费一颗鸡蛋,现有鸡蛋:6颗
消费一颗鸡蛋,现有鸡蛋:5颗
通知生产者开始工作!
生产了一颗鸡蛋,现有鸡蛋:6颗
生产了一颗鸡蛋,现有鸡蛋:7颗
生产了一颗鸡蛋,现有鸡蛋:8颗
生产了一颗鸡蛋,现有鸡蛋:9颗
消费一颗鸡蛋,现有鸡蛋:8颗
生产了一颗鸡蛋,现有鸡蛋:9颗
生产了一颗鸡蛋,现有鸡蛋:10颗
生产了一颗鸡蛋,现有鸡蛋:11颗
消费一颗鸡蛋,现有鸡蛋:10颗
生产了一颗鸡蛋,现有鸡蛋:11颗
生产了一颗鸡蛋,现有鸡蛋:12颗
生产了一颗鸡蛋,现有鸡蛋:13颗
消费一颗鸡蛋,现有鸡蛋:12颗
.....
代码托管:https://github.com/Oumuv/ThreadDemo
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值