多线程-等待唤醒机制经典案例-生产者消费者

class Resource{
	private String name;
	private int count;
	private boolean flag = false;
	
	public synchronized void set(String name){
		while(flag){//将if语句变为while语句,使得线程被唤醒后,能够再次进行flag的判断
			try{
				this.wait();
			}
			catch(InterruptedException e){
				
			}
		}
		this.name = name + count ;
		count++;
		System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
		
		flag = true;
		notifyAll();//仅仅是notify(),可能会唤醒同一个锁上的线程,从而出现死锁现象
		
	}
	
	public synchronized void out(){
		while(!flag){//将if语句变为while语句,使得线程被唤醒后,能够再次进行flag的判断
			try{
				this.wait();
			}
			catch(InterruptedException e){
				
			}
		}
		System.out.println(Thread.currentThread().getName()+".......消费者.."+this.name);
		
		flag = false;
		notifyAll();//仅仅是notify(),可能会唤醒同一个锁上的线程,从而出现死锁现象
	}
}

class Producer implements Runnable{
	private Resource r;
	
	Producer(Resource r){
		this.r = r;
	}
	public void run(){
		while(true){
			r.set("烤鸭");
		}
	}
}

class Consumer implements Runnable{
	private Resource r;
	
	Consumer(Resource r){
		this.r = r;
	}
	
	public void run(){
		while(true){
			r.out();
		}
	}
}

class ProConTest{
	public static void main(String [] args){
		Resource r = new Resource();
		
		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);
		
		Thread t0 = new Thread(pro);//多生产者,多消费者
		Thread t1 = new Thread(pro);
		
		Thread t2 = new Thread(con);
		Thread t3 = new Thread(con);
		

		t0.start();
		t1.start();
		t2.start();
		t3.start();
		/*
		多生产者多消费者可能出现的问题:
		1.生产了,但是没有被消费:(多生产)
		
			Thread-0...生产者...烤鸭697
			Thread-1...生产者...烤鸭698
			Thread-2.......消费者..烤鸭698
			Thread-0...生产者...烤鸭699
			Thread-1...生产者...烤鸭700
			Thread-2.......消费者..烤鸭700
			
		2.生产一个,被多次消费:(多消费)
		
			Thread-1...生产者...烤鸭387
			Thread-3.......消费者..烤鸭387
			Thread-2.......消费者..烤鸭387
			Thread-1...生产者...烤鸭388
			Thread-3.......消费者..烤鸭388
			Thread-2.......消费者..烤鸭388
		*/
	}
}


多生产多消费问题解析
先从生产烤鸭一开始,把代码走一遍:
生产了烤鸭一,flag 变为true,t0线程等待,t0一挂,此时活着的线程有三个,这三个处于临时阻塞状态,并且都有可能被CPU执行到,假设t1被执行到了,flag为true,t1也挂了。
接着,假设t2抢到了执行权,消费了烤鸭一,flag变为false,这时候notify(),此时线程池里有t0和t1,假设t0在这个时候活了,但此时是没有执行权的,此时t2继续执行,!flag为真,t2挂了。现在是t0和t3活着。
假设t3抢到执行权了,!flag为真,t3也挂了。 此时只有t0活着。

现在关键来了:

    if(flag){
	try{this.wait()}catch(InterruptedException e){}
    }

        t0活了以后,它还判判断标记么?
if语句判断一次就结束了,所以t0会继续往下走,生产烤鸭二


    this.name = name + count ;
	count++;
	System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);

flag变为true;
关键来了,此时notify();t1、2、3都有机会被唤醒,假设t1被唤醒了,flag为真,t0挂了,这时候t1执行,t1不判断标记,往下走,烤鸭三就出来了

        所以,会出现多生产的问题,多消费的问题也是类似:

t0、1、2都挂了,t3消费一次后,唤醒的不是t0、1,而是t2,就出现了多消费的情况。


解决方案:
我们希望,线程被唤醒后,能够进行多次判断,能够进行多次判断的是,while
所以,将if(flag)修改为while(flag)

但这个时候又出现了死锁问题:

	H:\JavaCode>java ProConTest
	Thread-0...生产者...烤鸭0
	Thread-3.......消费者..烤鸭0
	Thread-1...生产者...烤鸭1
	Thread-3.......消费者..烤鸭1
	Thread-0...生产者...烤鸭2


	H:\JavaCode>java ProConTest
	Thread-0...生产者...烤鸭0
	Thread-3.......消费者..烤鸭0
	Thread-1...生产者...烤鸭1
	Thread-3.......消费者..烤鸭1
	Thread-0...生产者...烤鸭2


	H:\JavaCode>java ProConTest
	Thread-0...生产者...烤鸭0
	Thread-3.......消费者..烤鸭0
	Thread-1...生产者...烤鸭1
	Thread-3.......消费者..烤鸭1
	Thread-0...生产者...烤鸭2
	



死锁问题分析:
假设现在的线程状态是,t0生产完了,t1、2、3都挂掉了,t0生产完后,flag变为true,notify();如果唤醒的是同锁的线程t1,因为while的存在,t0进行判断,挂了,t1进行判断,也挂了,这个时候,4个线程都挂掉了,就出现了死锁的状态。


死锁问题解决方案:
现在我们都知道,如果唤醒的是t2、3这两个线程中的任意一个, 就不会出现这样的问题,那么,我们可不可以指定唤醒某线程,遗憾的是,jdk1.5之前并没有提供这样的方法。
退而求其次,我们将所有的线程都唤醒notifyAll();,这样即使t1也挂了,仍然还有其它线程的存在。


小结:
生产者,消费者


多生产者,多消费者的问题
if判断标记,只有一次,会导致不该运行的线程运行了,出现了数据错误的情况
while判断标记,解决了线程获取执行权后,是否要运行的问题

notify:只能唤醒一个线程,如果本方唤醒本方,没有意义,而且while判断标记+notify()会导致线程死锁。
notifyAll解决了本方线程一定会唤醒对方线程的问题。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值