聊聊多线程(五)生产者消费者模式

超详细的Java知识点路线图


生产者消费者模式

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,所以便有了生产者和消费者模式。

线程的等待和通知

一旦线程进入同步块或同步方法,JVM会启动监视器监控线程的状态,线程都会持有锁,同步块持有是锁对象,同步方法的锁是this.

Object类的方法:

  • void wait() 让持有锁的线程进入等待状态,直到被通知
  • void wait(long time) 让线程等待,直到被通知或时间结束
  • void notify() 随机选择一个等待的线程,进行通知
  • void notifyAll() 通知所有等待的线程

注意:上面的方法只能是锁对象调用,否则出现异常IllegalMonitorStateException

案例代码

以做包子为例,假设仓库里最多能放100个包子,这个保存包子的仓库可以看做缓冲区,生产者做好包子后会放到仓库里,消费者则会从仓库中取出包子吃掉,当生产者做包子
太快,会把仓库装满,生产者就会等待;而消费者吃包子太快,仓库被取空了,消费者就会等待。

/**
 * 包子
 *
 */
public class Baozi {

	//包子编号
	private int id;
	public Baozi(int id){
		this.id = id;
	}
	
	public String toString(){
		return "包子#"+id;
	}
}

/**
 * 包子仓库
 * @author xray
 *
 */
public class BaoziStore {

	//最大数量
	public static final int MAX_NUM = 100;
	//包子集合
	private LinkedList<Baozi> baozis = new LinkedList<>();
	//锁对象
	private Object lock = new Object();
	
	/**
	 * 做包子
	 */
	public void makeBaozi(){
		//使用同步块
		synchronized (lock) {			
			//判断包子数量是否达到最大值
			if(baozis.size() == MAX_NUM){
				System.out.println("仓库满了,生产者等待:"+Thread.currentThread().getName());
				try {
					//让生产者等待
					lock.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}else{
				//如果仓库还有空间,让生产者继续生产
				System.out.println("仓库有空了,生产者继续做包子:"+Thread.currentThread().getName());
				lock.notifyAll();
			}
			//创建包子,添加到集合中
			Baozi baozi = new Baozi(baozis.size());
			baozis.push(baozi);
			System.out.println(Thread.currentThread().getName()+"生产者,做了"+baozi);
		}
	}
	
	/**
	 * 吃包子
	 */
	public void takeBaozi(){
		synchronized (lock) {
			//判断如果仓库为空,就让消费者等待
			if(baozis.size() == 0){
				System.out.println("仓库空了,消费者等一下:"+Thread.currentThread().getName());
				try {
					lock.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}else{
				System.out.println("仓库有包子了,消费者快来:"+Thread.currentThread().getName());
				//如果仓库有包子,就通知消费者来吃
				lock.notifyAll();
			}
			//从栈顶拿包子,然后删除掉
			if(baozis.size() > 0){
				Baozi baozi = baozis.pop();
				System.out.println(Thread.currentThread().getName()+"消费者吃掉了"+baozi);
			}
		}
	}
}

public class TestBaoziStore {

	public static void main(String[] args) {
		//仓库对象
		BaoziStore store = new BaoziStore();
		//创建生产者线程,生产100个包子
		Thread productor = new Thread(()->{
			for(int i = 0;i < 100;i++){
				store.makeBaozi();
			}
		});
		//启动生产者
		productor.start();
		//创建10个消费者,每个吃10个包子
		for(int i = 0;i < 10;i++){
			Thread consumer = new Thread(()->{
				for(int j = 0;j < 10;j++){
					store.takeBaozi();
				}
			});
			consumer.start();
		}
		
	}

}

上面的生产者消费者模式除了使用锁的等待和通知方式实现外,还可以使用阻塞队列,自动完成等待和通知功能。

阻塞队列介绍

阻塞队列在java.util.concurrent(并发包)中,用于解决大并发量情况下,数据的生产和消费速度不一致问题。

主要API:

  • BlockingQueue接口
    • void put(T t) 添加数据到末尾,队列满了会自动阻塞线程
    • T take() 从队列的头部取出数据并删除,队列空了会阻塞线程

BlockingQueue的主要实现类

  • ArrayBlockingQueue 数组阻塞队列
  • LinkedBlockingQueue 链表阻塞队列
案例代码
public class TestBaozi {

	public static void main(String[] args) {
		//创建链表阻塞队列
		LinkedBlockingQueue<Baozi> baozis = new LinkedBlockingQueue<>(10);
		//创建生产者线程,生产100个包子
		Thread productor = new Thread(()->{
			for(int i = 0;i < 100;i++){
				try {
					//创建包子,存入集合
					Baozi baozi = new Baozi(i);
					baozis.put(baozi);
					System.out.println(Thread.currentThread().getName()+"生产了"+baozi);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
		productor.setName("生产者");
		productor.start();
		//创建5个消费者,每个吃20个包子
		for(int i = 0;i < 5;i++){
			Thread consumer = new Thread(()->{
				for(int j = 0;j < 20;j++){
					try {
						//从集合拿包子
						Baozi baozi = baozis.take();
						System.out.println(Thread.currentThread().getName()+"吃掉了"+baozi);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});
			consumer.setName("消费者"+i);
			consumer.start();
		}
		
	}

}

结束


大家如果需要学习其他Java知识点,戳这里 超详细的Java知识点汇总

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恒哥~Bingo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值