生产者与消费者

一个生产者与一个消费者,要交替生产与消费。

package com.copy;

public class ProducerConsumerDemo {
    
    public static void main(String[] args){
        Resource res = new Resource();
        Producer p = new Producer(res);
        Consumer c = new Consumer(res);
        Thread t1 = new Thread(p,"Thread1");
        Thread t2 = new Thread(c,"Thread2");
        t1.start();
        t2.start();
    }
}

class Resource{
    private int n=1;
    private boolean flag = false;//生没有生产好        true,生产者停
    
    public synchronized void in(){
        if(flag){
            try{
                wait();//写不写this都一样
            }catch(InterruptedException e){
                System.out.println("aaa");
            }        
        }
        
        try{Thread.sleep(5);}catch(InterruptedException e){}
        System.out.println(Thread.currentThread().getName()+"..生产.."+n);
        flag = true;
        this.notify();//写不写this都一样
    }
    
    public synchronized void out(){
        if(!flag){
            try{
                wait();
            }catch(InterruptedException e){
                System.out.println("bbb");
            }        
        }
        
        try{Thread.sleep(5);}catch(InterruptedException e){}
        System.out.println(Thread.currentThread().getName()+"...消费..."+n);
        n++;
        flag = false;
        notify();
    }
}

class Producer implements Runnable{
    private Resource res;
    
    Producer(Resource res){
        this.res=res;
    }
    
    public void run(){
    //注意不能在这里synchronized,因为produer和consumer的对象不同。wait会报错。
        while(true){
            res.in();
        }
    }
}

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

 

如果有2个或者两个以上的消费者与生产者,如何交替生产与消费。

Thread t1 = new Thread(p,"Thread1");
Thread t1 = new Thread(p,"Thread2");
Thread t3 = new Thread(c,"Thread3");
Thread t4 = new Thread(c,"Thread4");
t1.start();
t2.start();
t3.start();
t4.start();

但是这样发现了问题,会重复生产或者重复消费。

t1获得执行资格,判断if,打印“生产90”,置flag为true。

t1获得执行资格,判断if,wait了。

t2获得执行资格,判断if,wait了。

t3获得执行资格,判断if,打印“消费90”,n变为91,置flag为false,notify了t1。

t1获得执行资格,接着wait的地方继续运行(不需要判断flag了),打印“生产91”,置flag为true,notify了t2.

t2获得执行资格,接着wait的地方继续运行,这时flag为true对它已经没用了,不需要判断了,打印“生产91”,notify(其实现在也没线程在等)。

所以出现了错误的结果。

因此要把两个if改为while,让线程每次被唤醒的时候都去判断flag。(这时候不要觉得会一直循环判断停不下来。为true,wait;为false,循环结束)

 

但这时运行,发现程序可能一直停着不动了。

t1获得执行资格,判断if,打印“生产1”,置flag为true。

t1获得执行资格,判断if,wait。

t2获得执行资格,判断if,wait。

t3获得执行资格,判断if,打印“消费1”,n变为2,置flag为true,notify了t1。

t4获得执行资格,判断if,wait。

t1获得执行资格,判断if,打印“生产2”,置flag为true,notify了t2。

t2获得执行资格,判断if,wait了。

t1获得执行资格,判断if,wait了。

现在只有t3不在wait。

t3获得执行资格,判断if,打印“消费2”,n变为3,置flag为false,notify了t4。

t3/t4判断,都wait了。现在t1/t2也在wait,所以程序停着不动了。

也就是说notify唤醒了本方(生产者或是消费者),在对方都wait的情况下一个都没唤醒,所以一直等待。(唤醒的本方while判断还是会wait,所以都wait了)

所以还要把两个notify改为notifyAll

 

多生产者,消费者的情况,用while和notifyAll。 

 

这种方法还可以改进。(JDK5.0)

 java.util.concurrent.locks类中有LockCondition两个接口,ReentrantLock类(实现了Lock接口)。

 Lock的锁定操作比synchronized更灵活,可以支持多个Condition对象。

Lock的方法,lock()unlock(),显式地加/释放锁。

Condition的方法,await()(throws InterruptedException)signal()signalAll()分别替代了wait()(throws InterruptedException)notify()notifyAll()这三种Object类的方法。

 

当拿到锁执行代码时,发生了异常,程序结束,但锁还没有释放,因此unlock操作要放到finally里。

 改进后的代码

class Resource{
	private int n=1;
	private boolean flag = false;
	private Lock lock = new ReentrantLock();
	private Condition condition_pro = lock.newCondition(); 
	private Condition condition_con = lock.newCondition(); 
	
	public void in() throws InterruptedException{
		lock.lock();
		
		try{
			while(flag){
				condition_pro.await();
			}		
			System.out.println(Thread.currentThread().getName()+"..生产.."+n);
			flag = true;
			condition_con.signal();//只唤醒对方
		}finally{
			lock.unlock();
		}
			
	}
	
	public void out()throws InterruptedException{
		lock.lock();
		
		try{
			while(!flag){		
				condition_con.await();
			}	
			System.out.println(Thread.currentThread().getName()+"...消费..."+n);
			n++;
			flag = false;
			condition_pro.signal();//只唤醒对方
		}finally{
			lock.unlock();
		}
				
	}
}

class Producer implements Runnable{
	private Resource res;
	
	Producer(Resource res){
		this.res=res;
	}
	
	public void run(){
		while(true){
			try{//throws之后,这里要处理
				res.in();	
			}catch(InterruptedException e){
			}		
		}
	}
}

class Consumer implements Runnable{
	private Resource res;
	
	Consumer(Resource res){
		this.res=res;
	}
	
	public void run(){
		while(true){
			try{//throws之后,这里要处理
				res.out();	
			}catch(InterruptedException e){
			}		
		}
	}
}

前面synchronized的wait和notifyAll(如果没有synchronized,notify会IllegalMonitorStateException,毕竟有锁才会有唤醒等待机制)唤醒了对方和本方的所有等待线程,这样不好,只要唤醒对方的就好了。这是condition的好处。

posted @ 2017-11-17 15:58  EdwardChu  阅读(226)  评论(0编辑  收藏  举报