温故一下上一节所学习的生产者消费者代码:
两个线程时:
通过标志位flag的if判断和同步函数互斥较好解决两个线程,一个生产者、一个消费者交替执行的功能
类名:ProducterConsumerDemo.java
代码:
1 class ProducterConsumerDemo 2 { 3 public static void main(String[] args) 4 { 5 Resources r =new Resources(); 6 Productor pro =new Productor(r); 7 Consumer con = new Consumer(r); 8 9 Thread t1 =new Thread(pro); 10 Thread t2 =new Thread(con); 11 t1.start(); 12 t2.start(); 13 System.out.println("Hello World!"); 14 } 15 } 16 17 class Resources 18 { 19 private String name; 20 private int count =1; 21 private boolean flag =false; 22 23 public synchronized void set(String name) 24 { 25 if(flag) 26 try{this.wait();}catch(Exception e){} 27 this.name = name+"--"+count++; 28 29 System.out.println(Thread.currentThread().getName()+"生产者"+this.name); 30 flag =true; 31 //唤醒对方进程 32 this.notify(); 33 34 } 35 public synchronized void out() 36 { 37 if(!flag) 38 try{this.wait();}catch(Exception e){} 39 40 System.out.println(Thread.currentThread().getName()+" ....消费者...."+this.name); 41 flag =false; 42 //唤醒对方进程 43 this.notify(); 44 45 } 46 } 47 48 class Productor implements Runnable 49 { 50 private Resources res; 51 Productor(Resources res){ 52 this.res =res; 53 } 54 public void run(){ 55 while(true){ 56 res.set("++商品++"); 57 } 58 } 59 60 } 61 62 class Consumer implements Runnable 63 { 64 private Resources res; 65 Consumer(Resources res){ 66 this.res =res; 67 } 68 public void run(){ 69 while(true){ 70 res.out(); 71 } 72 } 73 74 }
如果再多加上两个线程呢?
即把其中的ProducterConsumerDemo类改为如下:
class ProducterConsumerDemo2 { public static void main(String[] args) { Resources r =new Resources(); Productor pro =new Productor(r); Consumer con = new Consumer(r); Thread t1 =new Thread(pro); Thread t2 =new Thread(pro);//多个生产者 Thread t3 =new Thread(con); Thread t4 =new Thread(con);//多个消费者 t1.start(); t2.start(); t3.start(); t4.start(); System.out.println("Hello World!"); } }
运行后发现,加上t3和t4之后结果就错了。
为什么两个线程的时候执行结果正确而四个线程的时候就不对了呢?
因为线程在wait()的时候,接收到其他线程的通知,即往下执行,不再进行判断。两个线程的情况下,唤醒的肯定是另一个线程;但是在多个线程的情况下,执行结果就会混乱无序。
比如,一个可能的情况是,一个增加线程执行的时候,其他三个线程都在wait,这时候第一个线程调用了notify()方法,其他线程都将被唤醒,然后执行各自的增加或减少方法。
解决的方法就是:在被唤醒之后仍然进行条件判断,去检查要改的数字是否满足条件,如果不满足条件就继续睡眠。
1)把两个方法中的if改为while即可。
2)需要将notify()改成notifyAll()
代码:
1 /* 2 ProducterConsumerDemo解决了只有两个线程共享资源的生产消费问题,主要利用标志位的互斥解决 3 4 本程序致力于解决多出现多个生产者,多个消费者的时候,依然能够达到生产一次,消费一次的功能 5 : 6 解决的方法就是:1)在被唤醒之后仍然进行条件判断,去检查要改的数字是否满足条件,如果不满足条件就继续睡眠。把两个方法中的if改为while即可。 7 当然,此时仍会出现问题,就是所以线程都等待,失去资格 8 2)需要将notify()改成notifyAll() 9 */ 10 11 class ProducterConsumerDemo2 12 { 13 public static void main(String[] args) 14 { 15 Resources r =new Resources(); 16 Productor pro =new Productor(r); 17 Consumer con = new Consumer(r); 18 19 Thread t1 =new Thread(pro); 20 Thread t2 =new Thread(pro);//多个生产者 21 Thread t3 =new Thread(con); 22 Thread t4 =new Thread(con);//多个消费者 23 t1.start(); 24 t2.start(); 25 t3.start(); 26 t4.start(); 27 System.out.println("Hello World!"); 28 } 29 } 30 31 class Resources 32 { 33 private String name; 34 private int count =1; 35 private boolean flag =false; 36 37 public synchronized void set(String name) 38 { //1)循环判断 39 while(flag) 40 try{this.wait();}catch(Exception e){} 41 this.name = name+"--"+count++; 42 43 System.out.println(Thread.currentThread().getName()+"生产者"+this.name); 44 flag =true; 45 //2)唤醒所有进程 46 this.notifyAll(); 47 48 } 49 public synchronized void out() 50 { 51 //1)循环判断 52 while(!flag) 53 try{this.wait();}catch(Exception e){} 54 55 System.out.println(Thread.currentThread().getName()+" ....消费者...."+this.name); 56 flag =false; 57 //2)唤醒所有进程 58 this.notifyAll(); 59 60 } 61 } 62 63 class Productor implements Runnable 64 { 65 private Resources res; 66 Productor(Resources res){ 67 this.res =res; 68 } 69 public void run(){ 70 while(true){ 71 res.set("++商品++"); 72 } 73 } 74 75 } 76 77 class Consumer implements Runnable 78 { 79 private Resources res; 80 Consumer(Resources res){ 81 this.res =res; 82 } 83 public void run(){ 84 while(true){ 85 res.out(); 86 } 87 } 88 89 }
效果: