生产者和消费者问题是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 }
原文:http://www.cnblogs.com/wsw-tcsygrwfqd/p/6395377.html