生产者与消费者
一个生产者与一个消费者,要交替生产与消费。
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类中有Lock和Condition两个接口,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的好处。