这个是java线程学习里面比较经典的一个模型,描述的是多个线程之间同步与让步的问题
情景:
角色1:产品->窝头
角色2:产品容器->桶
角色3:生产者->生产窝头->放在桶里
角色4:消费者->从桶里拿出窝头->吃掉
上代码:
public class ProducerConsumer { public static void main(String[] args){ SynStack ss=new SynStack(); Producer p=new Producer(ss); Consumer c=new Consumer(ss); new Thread(p,"Producer1").start(); new Thread(c,"Consumer").start(); }}/** * 窝头 * volador * */class WoTo{ int id; WoTo(int id){ this.id=id; } public String toString(){ return "WoTo :"+this.id; }}/** * 装窝头的桶 * volador * */class SynStack{ int index=0; WoTo[] arrWT=new WoTo[6]; /** * 往桶里塞一个窝头 * @param wt */ public synchronized void push(WoTo wt){ while(index == arrWT.length){ try { this.wait(); //窝头满了,先停会 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } this.arrWT[index]=wt; this.index++; this.notify(); //弄好窝头了,赶紧叫醒那些等吃的人来吃 } /** * 从桶里拿出一个窝头 * */ public synchronized WoTo pop(){ while(this.index==0){ try { this.wait(); //没窝头了,先睡会 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } this.notify(); //窝头吃掉了,叫醒那个弄窝头的,赶紧弄窝头去 this.index--; return this.arrWT[index]; }}/** * 生产者,生产窝头 * volador * */class Producer implements Runnable{ SynStack ss=null; Producer(SynStack ss){ this.ss=ss; } @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<20;i++){ //只生产20个窝头 WoTo wt=new WoTo(i); ss.push(wt); System.out.println("生产了:"+wt); try { Thread.sleep((int)Math.random()*1000); //累了,小睡一会 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }}/** * 消费者,专门吃窝头的 * volador * */class Consumer implements Runnable{ SynStack ss=null; Consumer(SynStack ss){ this.ss=ss; } @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<20;i++){ //只吃20个窝头 WoTo wt=this.ss.pop(); System.out.println("消费了:"+wt); try { Thread.sleep((int)Math.random()*1000); //吃了1个,小睡一会 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }}
运行结果:
消费了:WoTo :0 生产了:WoTo :0 消费了:WoTo :1 生产了:WoTo :1 消费了:WoTo :2 生产了:WoTo :2 消费了:WoTo :3 生产了:WoTo :3 生产了:WoTo :4 消费了:WoTo :4 生产了:WoTo :5 消费了:WoTo :5 生产了:WoTo :6 消费了:WoTo :6 生产了:WoTo :7 消费了:WoTo :7 生产了:WoTo :8 消费了:WoTo :8 生产了:WoTo :9 消费了:WoTo :9 生产了:WoTo :10 消费了:WoTo :10 生产了:WoTo :11 消费了:WoTo :11 生产了:WoTo :12 消费了:WoTo :12 生产了:WoTo :13 消费了:WoTo :13 生产了:WoTo :14 消费了:WoTo :14 生产了:WoTo :15 消费了:WoTo :15 生产了:WoTo :16 消费了:WoTo :16 生产了:WoTo :17 消费了:WoTo :17 生产了:WoTo :18 消费了:WoTo :18 生产了:WoTo :19 消费了:WoTo :19
由于生产者,消费者睡的时间差不多,就出现这种生产一个就吃掉一个的现象。
上面有几点东西要注意:
1.从桶里面放窝头跟从桶里面拿窝头出来必须要同步,设想一下,假如一个生产者生产了一个窝头,并往桶里面放了,下一步准备更新桶里面的窝头数,与此同时,一个吃窝头的家伙也进了桶里,在生产者添加记录数之前,把窝头给吃了,生产者还不知情,好吧,一个窝头就这样离奇消失了、、
2.生产者或者消费者要操作桶里的窝头前先要检测窝头数量情况,这里一定要用while循环去检测,不能用if,因为if判断完后还会往下面执行,都没窝头了还执行什么??
3.生产者或者消费者发现桶里的情况不对的时候要睡一下觉,同时把锁让出来给别人。这里要用wait方法睡觉,这个方法睡了,它就会把这个对象上的锁让出来,让别的等待在这个对象上的线程能拿到这把钥匙继续进行操作,记住,wait后要记得用notify把他叫醒。
分析下这个模式的运行:
首先生成桶、生产者、消费者。然后生产者、消费者同时开始工作,一个不停地往桶里面加窝头,另一个不停地从桶里面拿包子,吃掉。生产者使用桶的时候,拿到了桶的对象锁,消费者就不能动桶了。桶里消费者使用桶的生产者也不能动桶。ok,假如生产者比较积极,很快就把桶装满了,消费者还没来得及吃掉,这时,生产者就会用wait方法睡觉,等消费者吃了,再继续工作。注意,生产者在睡的同时要把桶的对象锁让出来,要不你死占着锁,消费者进不了桶,怎么吃?这就是为什么要用wait方法睡觉的原因了,他在睡前会把锁让出来。ok,继续,生产者总不能一直睡下去吧,消费者吃完了怎么办?所以,消费者在吃了后,桶有空间了,就要调用notify,摇一下铃,叫醒正在睡觉的生产者,让他起床造窝头。同理假如消费者比较贪吃,很快吃完了,也会睡觉,生产者造了窝头之后,也会用notify叫醒正在睡觉的消费者起床吃东西了。然后一直这样循环,知道搞定20个窝头,程序退出。
wait必须要synchronized里面调用才有效、、、、