生产者和消费者指的是两个不同的线程类对象,操作统一资源的情况。具体的操作流程如下:
(1)生产者负责生成数据,消费者负责取走数据;
(2)生产者每生产完一组数据之后,消费者就要取走一组数据。
一. 直白写法
1. info类
1 public class Info { 2 private String name; 3 private double price; 4 public String getName() { 5 return name; 6 } 7 public void setName(String name) { 8 this.name = name; 9 } 10 public double getPrice() { 11 return price; 12 } 13 public void setPrice(double price) { 14 this.price = price; 15 } 16 17 }
2. 生产者类
1 public class Producer implements Runnable{ 2 private Info info; 3 public Producer(Info info) { 4 this.info=info; 5 } 6 7 @Override 8 public void run() { 9 for(int i=0;i<20;i++) { 10 if(i%2==0) { 11 this.info.setName("铅笔"); 12 try { 13 Thread.sleep(100); 14 } catch (InterruptedException e) { 15 // TODO Auto-generated catch block 16 e.printStackTrace(); 17 } 18 this.info.setPrice(1.0); 19 }else { 20 this.info.setName("LV"); 21 try { 22 Thread.sleep(100); 23 } catch (InterruptedException e) { 24 // TODO Auto-generated catch block 25 e.printStackTrace(); 26 } 27 this.info.setPrice(10000.0); 28 } 29 } 30 } 31 32 }
3. 消费者类
1 public class Consumer implements Runnable{ 2 private Info info; 3 public Consumer(Info info) { 4 this.info=info; 5 } 6 @Override 7 public void run() { 8 for(int i=0;i<20;i++) { 9 try { 10 Thread.sleep(100); 11 } catch (InterruptedException e) { 12 // TODO Auto-generated catch block 13 e.printStackTrace(); 14 } 15 System.out.println(this.info.getName()+"----"+this.info.getPrice()); 16 } 17 } 18 }
4. 测试类
1 public class Test { 2 public static void main(String[] args) { 3 Info info=new Info(); 4 new Thread(new Producer(info)).start(); 5 new Thread(new Consumer(info)).start(); 6 } 7 }
1 LV----1.0 2 铅笔----10000.0 3 铅笔----1.0 4 铅笔----10000.0 5 铅笔----1.0 6 LV----1.0 7 铅笔----1.0 8 LV----10000.0 9 铅笔----1.0 10 LV----1.0 11 铅笔----1.0 12 LV----10000.0 13 铅笔----10000.0 14 LV----10000.0 15 铅笔----10000.0 16 LV----10000.0 17 铅笔----1.0 18 LV----1.0 19 铅笔----1.0 20 LV----10000.0
说明:上面添加了sleep方法,是为了更好的观察分析存在的问题。根据测试结果我们可以知道,有两个很严重的问题。第一个是数据错乱,比如铅笔对应的是10000;第二个是数据重复设置,比如铅笔连着打印了几次。不符合生产一个,然后再消费一个的标准。因此,需要进行改进。
二. 解决数据错乱
数据的错乱完全是因为非同步的操作完成的,所以应该使用同步处理。因为取和设置是两个不同的操作,要想进行同步操作,因此需要将它们定义再一个类里面。
1. info类
1 public class Info { 2 private String name; 3 private double price; 4 public synchronized void set(String name,double price) { 5 this.name=name; 6 try { 7 Thread.sleep(100); 8 } catch (InterruptedException e) { 9 // TODO Auto-generated catch block 10 e.printStackTrace(); 11 } 12 this.price=price; 13 } 14 15 public synchronized void get() { 16 try { 17 Thread.sleep(100); 18 } catch (InterruptedException e) { 19 // TODO Auto-generated catch block 20 e.printStackTrace(); 21 } 22 System.out.println(this.name+"---------"+this.price); 23 } 24 25 }
2.生产者
1 public class Producer implements Runnable{ 2 private Info info; 3 public Producer(Info info) { 4 this.info=info; 5 } 6 7 @Override 8 public void run() { 9 for(int i=0;i<20;i++) { 10 if(i%2==0) { 11 this.info.set("铅笔", 1.0); 12 }else { 13 this.info.set("LV", 10000.0); 14 } 15 } 16 } 17 18 }
3.消费者
1 public class Consumer implements Runnable{ 2 private Info info; 3 public Consumer(Info info) { 4 this.info=info; 5 } 6 @Override 7 public void run() { 8 for(int i=0;i<20;i++) { 9 this.info.get(); 10 } 11 } 12 }
4. 测试类
1 public class Test { 2 public static void main(String[] args) { 3 Info info=new Info(); 4 new Thread(new Producer(info)).start(); 5 new Thread(new Consumer(info)).start(); 6 } 7 }
1 铅笔---------1.0 2 铅笔---------1.0 3 LV---------10000.0 4 LV---------10000.0 5 LV---------10000.0 6 LV---------10000.0 7 LV---------10000.0 8 铅笔---------1.0 9 铅笔---------1.0 10 铅笔---------1.0 11 LV---------10000.0 12 LV---------10000.0 13 铅笔---------1.0 14 铅笔---------1.0 15 铅笔---------1.0 16 LV---------10000.0 17 LV---------10000.0 18 铅笔---------1.0 19 铅笔---------1.0 20 铅笔---------1.0
说明:现在数据错乱的问题解决了,但是数据重复设置的问题更严重了。需要进一步解决
三. 解决重复设置问题
解决重复设置问题就需要有一个标志位来告诉是该生产还是是该消费。比如下面修改就可以解决:
1. Info类
1 package com.test.b; 2 3 public class Info { 4 private String name; 5 private double price; 6 private boolean flag=true; 7 //flag=true:表示可以生产,但是不可以取走 8 //flag=false:表示可以取走,但是不可以生产 9 public synchronized void set(String name,double price) { 10 //重复进入到了set()方法里面,发现不能够生产,因此需要进行等待 11 if(flag==false) { 12 try { 13 super.wait(); 14 } catch (InterruptedException e) { 15 // TODO Auto-generated catch block 16 e.printStackTrace(); 17 } 18 } 19 this.name=name; 20 try { 21 Thread.sleep(100); 22 } catch (InterruptedException e) { 23 // TODO Auto-generated catch block 24 e.printStackTrace(); 25 } 26 this.price=price; 27 this.flag=false;//修改生产标记,说明设置完毕,现在可以进行消费了 28 super.notify();//唤醒其它等待线程 29 } 30 31 public synchronized void get() { 32 if(this.flag==true) {//还没生产呢,但是进入到了get方法里面来,需要等待 33 try { 34 super.wait(); 35 } catch (InterruptedException e) { 36 // TODO Auto-generated catch block 37 e.printStackTrace(); 38 } 39 } 40 try { 41 Thread.sleep(100); 42 } catch (InterruptedException e) { 43 // TODO Auto-generated catch block 44 e.printStackTrace(); 45 } 46 System.out.println(this.name+"---------"+this.price); 47 this.flag=true;//消费完毕,说明现在可以进行生产了 48 super.notify(); 49 } 50 51 }
2. 生产者
1 public class Producer implements Runnable{ 2 private Info info; 3 public Producer(Info info) { 4 this.info=info; 5 } 6 7 @Override 8 public void run() { 9 for(int i=0;i<20;i++) { 10 if(i%2==0) { 11 this.info.set("铅笔", 1.0); 12 }else { 13 this.info.set("LV", 10000.0); 14 } 15 } 16 } 17 18 }
3. 消费者
1 public class Consumer implements Runnable{ 2 private Info info; 3 public Consumer(Info info) { 4 this.info=info; 5 } 6 @Override 7 public void run() { 8 for(int i=0;i<20;i++) { 9 this.info.get(); 10 } 11 } 12 }
4. 测试
1 public class Test { 2 public static void main(String[] args) { 3 Info info=new Info(); 4 new Thread(new Producer(info)).start(); 5 new Thread(new Consumer(info)).start(); 6 } 7 }
1 铅笔---------1.0 2 LV---------10000.0 3 铅笔---------1.0 4 LV---------10000.0 5 铅笔---------1.0 6 LV---------10000.0 7 铅笔---------1.0 8 LV---------10000.0 9 铅笔---------1.0 10 LV---------10000.0 11 铅笔---------1.0 12 LV---------10000.0 13 铅笔---------1.0 14 LV---------10000.0 15 铅笔---------1.0 16 LV---------10000.0 17 铅笔---------1.0 18 LV---------10000.0 19 铅笔---------1.0 20 LV---------10000.0
Note:生产者和消费者都需要实现Runnable接口。