您的位置:首页 > 其它

生产者消费者问题

2017-04-06 15:52 351 查看
生产者和消费者问题是java多线程中的等待唤醒机制的经典案例。
下面通过几个例子,循序渐进得了解这个问题。

1.单生产者和单消费者

1 class Res
2 {
3     private String name;
4     private int count=0;
5     private boolean flag=false;//用来标志资源是否为空
6     public Res(String name)
7     {
8         this.name=name;
9     }
10     //为其他类提供实现资源存放的方法
11     public synchronized void  set()
12     {
13         //如果资源内容是满的
14         //此时执行的线程,即生产者线程等待
15         if(flag)
16         {    try
17             {
18                 this.wait();
19             }
20             catch (Exception e)
21             {
22                 System.out.println("出错了");
23             }
24         }
25         //否则,资源内容为空
26         //新生产一个产品
27         count++;
28         System.out.println("生产者生产了"+name+count);
29         //修改标志
30         flag=true;
31         //唤醒在此对象监视器上等待的某个线程,即消费者线程
32         //第一次执行时,没有等待线程,为空唤醒
33         this.notify();
34     }
35
36     //为其他类提供资源提取的方法
37     public synchronized void  out()
38     {
39         //如果资源内容是空的
40         //此时执行的线程,即消费者线程等待
41         if(!flag)
42         {    try
43             {
44                 this.wait();
45             }
46             catch (Exception e)
47             {
48                 System.out.println("出错了");
49             }
50         }
51         //否则,资源内容存在
52         //则消费该产品
53         System.out.println("消费者消费了~~~~~~~~~"+name+count);
54         //修改标志
55         flag=false;
56         //唤醒在此对象监视器上等待的某个线程,即生产者线程
57         this.notify();
58     }
59 }
60 class Producer1 implements Runnable
61 {
62     Res r;
63     public Producer1(Res r)
64     {
65         this.r=r;
66     }
67
68 //实现run方法
69     public void run()
70     {
71
72         while(true)
73         {
74             r.set();
75         }
76     }
77
78 }
79 class Customer1 implements Runnable
80 {
81     Res r;
82     public Customer1(Res r)
83     {
84         this.r=r;
85     }
86
87     //实现run方法
88     public void run()
89     {
90         while(true)
91         {
92             r.out();
93         }
94     }
95 }
96 public  class  ProducerAndCustomerDemo1
97 {
98     public static void main(String[] args)
99     {
100         //建立资源对象
101         Res r=new Res("冰淇淋");
102         //创建生产者
103         Producer1 pro=new Producer1(r);
104         //创建消费者
105         Customer1 con=new Customer1(r);
106
107         //建立线程
108         Thread t1=new Thread(pro);
109         Thread t2=new Thread(con);
110         //开启线程
111         t1.start();
112         t2.start();
113
114     }
115 }


运行如下:



 

2.多生产者和多消费者问题

多生产者和多消费者的情况,如果仍然按照上述写法来写,会发生两种错误:

第一种,输出表现是某个产品被消费了多次或者某个产品没有被消费。

产生原因是,线程被唤醒后接着往下执行,没有再判断flag标记。

解决办法:把if语句,改为while循环。

这时,又会产生第二种错误,死锁。

输出表现:程序自行停止。

产生原因:线程进行唤醒动作时,唤醒了本类线程(例如,消费者线程唤醒了消费者线程),

没能达到唤醒对方的效果(例如,消费者线程应该唤醒生产者线程),而本类线程在进行标志位判断后

,也进入了等待状态,最终致使所有线程都在等待状态,程序停止。

解决办法:把notify语句改为notifyAll,确保线程每次执行唤醒动作时,都能达到唤醒对方的效果。

1 class Res
2 {
3     private String name;
4     private int count=0;
5     private boolean flag=false;
6     public Res(String name)
7     {
8         this.name=name;
9     }
10
11     public synchronized void  set()
12     {
13         //while循环判断标志位
14         while(flag)
15         {    try
16             {
17                 this.wait();
18             }
19             catch (Exception e)
20             {
21                 System.out.println("出错了");
22             }
23         }
24
25         count++;
26         System.out.println("生产者"+Thread.currentThread().getName()+"生产了"+name+count);
27         flag=true;
28         //唤醒在此对象监视器上等待的所有线程
29         this.notifyAll();
30     }
31
32     public synchronized void  out()
33     {
34         //while循环判断标志位
35         while(!flag)
36         {    try
37             {
38                 this.wait();
39             }
40             catch (Exception e)
41             {
42                 System.out.println("出错了");
43             }
44         }
45         System.out.println("消费者"+Thread.currentThread().getName()+"消费了~~~~~~~~~"+name+count);
46         flag=false;
47         //唤醒在此对象监视器上等待的所有线程
48         this.notifyAll();
49     }
50 }
51 class Producer1 implements Runnable
52 {
53     Res r;
54     public Producer1(Res r)
55     {
56         this.r=r;
57     }
58
59     public void run()
60     {
61
62         while(true)
63         {
64             r.set();
65         }
66     }
67
68 }
69 class Customer1 implements Runnable
70 {
71     Res r;
72     public Customer1(Res r)
73     {
74         this.r=r;
75     }
76
77     public void run()
78     {
79         while(true)
80         {
81             r.out();
82         }
83     }
84 }
85 public  class  ProducerAndCustomerDemo2
86 {
87     public static void main(String[] args)
88     {
89         //建立资源对象
90         Res r=new Res("冰淇淋");
91         //创建生产者
92         Producer1 pro=new Producer1(r);
93         //创建消费者
94         Customer1 con=new Customer1(r);
95
96         //建立线程
97         Thread t0=new Thread(pro);
98         Thread t1=new Thread(pro);
99         Thread t2=new Thread(con);
100         Thread t3=new Thread(con);
101         //开启线程
102         t0.start();
103         t1.start();
104         t2.start();
105         t3.start();
106
107     }
108 }


运行如下:



 

3.多生产者和多消费者问题(JDK1.5的新特性)

两个代替:

JDK1.5的新特性
用Lock接口的实现类,封装锁对象,显式完成申请锁和释放锁的动作,以此代替synchronized同步方法的使用;
Lock实现类的锁对象,最大的好处是,一个锁可以有多个监视器(即Condition对象,而同步代码块的锁只能有一个监视器),
这样就可以分别为生产者和消费者建立不同的监视器,实现只唤醒对方的操作,用指定监视器的singal方法代替了notifyAll方法。

1 import java.util.concurrent.locks.*;
2 class Rec
3 {
4     private String name;
5     private int count=0;
6     private boolean flag=false;
7     //建立锁对象
8     Lock lock=new ReentrantLock();
9     //得到该锁对象的两个监视器
10     //分别用于生产者和消费者
11     Condition producer_con=lock.newCondition();
12     Condition customer_con=lock.newCondition();
13
14     public Rec(String name)
15     {
16         this.name=name;
17     }
18     public void set()
19     {
20         //获取锁
21         lock.lock();
22         try
23         {
24             while(flag)
25             try
26             {
27                 //调用Condition对象的await方法
28                 producer_con.await();
29             }
30             catch (Exception e)
31             {
32                 System.out.println("有错误");
33             }
34             count++;
35             System.out.println(Thread.currentThread().getName()+"生产者~~JDK1.5~~,生产出了"+name+count);
36
37             flag=true;
38             //用消费者线程的监视器唤醒消费者
39             customer_con.signal();
40         }
41         /*加入try finally结构
42         原因:如果try代码块中出现异常,释放锁的动作就不会被执行
43         因此把释放锁动作放在finally中,确保其执行。
44
45         */
46         finally
47         {
48             //释放锁
49             lock.unlock();
50         }
51     }
52
53     public void out()
54     {
55         //获取锁
56         lock.lock();
57         try
58         {
59             while(!flag)
60             try
61             {
62                 customer_con.await();
63             }
64             catch (Exception e)
65             {
66                     System.out.println("有错误");
67
68             }
69
70         System.out.println(Thread.currentThread().getName()+"消费者~~~~~~~~~JDK1.5~~~~~~~~~~吃掉了"+name+count);
71         flag=false;
72         //用生产者线程的监视器唤醒生产者
73         producer_con.signal();
74         }
75         finally
76         {
77             //释放锁
78             lock.unlock();
79         }
80
81
82     }
83
84
85 }
86 class Producer implements Runnable
87 {
88     Rec r;
89     public Producer(Rec r)
90     {
91         this.r=r;
92     }
93
94     public void run()
95     {
96         while(true)
97         {
98             r.set();
99         }
100     }
101 }
102 class Customer implements Runnable
103 {
104     Rec r;
105     public Customer(Rec r)
106     {
107         this.r=r;
108     }
109
110     public void run()
111     {
112         while(true)
113         {
114             r.out();
115         }
116     }
117 }
118 public class  ProAndConLockSolution
119 {
120     public static void main(String[] args)
121     {
122         Rec r=new Rec("北京烤鸭");
123         Producer pro1=new Producer(r);
124         Producer pro2=new Producer(r);
125         Customer con1=new Customer(r);
126         Customer con2=new Customer(r);
127
128         Thread t0=new Thread(pro1);
129         Thread t1=new Thread(pro2);
130         Thread t2=new Thread(con1);
131         Thread t3=new Thread(con2);
132
133         t0.start();
134         t1.start();
135         t2.start();
136         t3.start();
137
138
139     }
140 }


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: