[java]深入理解生产者消费者模型

  • 生产者消费者模型不必多说,在操作系统课和java多线程都已经提到过。而你是否真的懂wait,notify,notifyAll使用的细节呢?下面我们一起来探究一下

这是一个典型的生产者消费者模型这样写是完全符合线程安全的!
这段代码很容易引申出来两个问题:一个是wait()方法外面为什么是while循环而不是if判断,另一个是结尾处的为什么要用notifyAll()方法,用notify()行吗?

/**
 * 生产者 和 消费者模型
 * wait notify notifyAll 再探究
 */
public class ProducerandConsumer {
	public static void main(String[] args) {
		Resource r = new Resource();
		for(int i=0;i<10;i++){
			//开启十个生产者线程
			new Thread(new Consumer(r)).start();
		}
		for(int i=0;i<10;i++){
			//开启十个生产者线程
			new Thread(new Producer(r)).start();
		}
	}
	
}

class Resource {
	Stack<Object> storehouse = new Stack<Object>();
	int index=0;
	public synchronized void consume(){
		while(storehouse.isEmpty()){
			try {
				this.wait();
			} catch (InterruptedException e) {}
		}
			System.out.println("消费第"+storehouse.size()+"个产品");
			storehouse.pop();
			this.notifyAll();
	}
	public synchronized void produce(){
		while(storehouse.size()==50){
			try {
				this.wait();
			} catch (InterruptedException e) {e.printStackTrace();}
		}
			Object o  = new Object();
			storehouse.push(o);
			System.out.println("生产第"+storehouse.size()+"个产品");
			this.notifyAll();
	}
}

class Consumer implements Runnable{
	Resource r;
	public void run() {
		while(true){
			r.consume();
		}
	}
	public Consumer(Resource r){
		this.r = r;
	}
	
}
class Producer implements Runnable{
	Resource r;
	public void run() {
		while(true){
			r.produce();
		}
	}
	public Producer(Resource r){
		this.r = r;
	}
	
}

一.探究为什么要在wait()方法前面加while用来判断
比如说现在仓库已经满了,
1.生产者1线程进入生产,被wait掉。
2.生产者2进入生产,被wait掉。
3.消费者1进入消费,仓库产品消费。
4.接着notifyAll所有线程
5.生产者1被唤醒->生产 仓库生产满了,
6. 此时生产者2还处于就绪状态且不需要判断标记直接就可以就行生产,造成了线程安全问题

就像在本例中,如果只有一个生产者线程,一个消费者线程,那其实是可以用if代替while的,因为线程调度的行为是开发者可以预测的,生产者线程只有可能被消费者线程唤醒,反之亦然,因此被唤醒时条件始终满足,程序不会出错。但是这种情况只是多线程情况下极为简单的一种,更普遍的是多个线程生产,多个线程消费,那么就极有可能出现唤醒生产者的是另一个生产者或者唤醒消费者的是另一个消费者,这样的情况下用if就必然会现类似过度生产或者过度消费的情况

二.探究为什么要用notifyAll()而不是notify()

notify()和notifyAll()的区别是,唤醒一个线程和唤醒全部线程
在多生产者,多消费者模型中,必须要用notifyAll()来唤醒。
为什么?为了防止死锁
当生产者或消费者一方全部线程到冻结状态,而另一方线程其中之一唤醒了自己方线程,再次经过wait判断导致所有线程全部冻结导致死锁。
比如现在仓库为空 有三个线程 消费者1,消费者2,生产者1
仓库为空
步骤1,消费者1,消费者2进入判断标记全部都挂起
步骤2,生产者1正常运行仓库为满,唤醒消费者1,然后生产者1又抢到锁,接着生产者1又进入被挂起
步骤3,消费者1进入消费,此时等待池中又消费者2,生产者1,如果消费者1,消费完成后仓库为空,唤醒消费者2,此时消费者2也被挂起,而没有线程可以调用notify来唤醒生产者1了。所以造成死锁现象

加深理解

我们要明白java中对象锁的模型,JVM会为一个使用内部锁(synchronized)的对象维护两个集合,Entry Set和Wait Set,也有人翻译为锁池和等待池,意思基本一致。

对于Entry Set:如果线程A已经持有了对象锁,此时如果有其他线程也想获得该对象锁的话,它只能进入Entry Set,并且处于线程的BLOCKED状态。

对于Wait Set:如果线程A调用了wait()方法,那么线程A会释放该对象的锁,进入到Wait Set,并且处于线程的WAITING状态。

对于Entry Set中的线程,当对象锁被释放的时候,JVM会唤醒处于Entry Set中的某一个线程,这个线程的状态就从BLOCKED转变为RUNNABLE。

对于Wait Set中的线程,当对象的notify()方法被调用时,JVM会唤醒处于Wait Set中的某一个线程,这个线程的状态就从WAITING转变为RUNNABLE;或者当notifyAll()方法被调用时,Wait Set中的全部线程会转变为RUNNABLE状态。所有Wait Set中被唤醒的线程会被转移到Entry Set中。

notify notifyAll方法唤醒的是等待池中的线程,notifyAll被调用后等待池中的线程全部进入锁池
当对象锁被释放时 锁池中的线程相互抢占锁

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值