多线程生产者/消费者模式实现

参考书籍《java多线程编程核心技术》

都是基于wait/notify实现的

一个生产者和一个消费者:操作值

1 package com.qf.test10.pojo;
2 
3 /**
4  * @author qf
5  * @create 2018-09-18 15:59
6  */
7 public class Entity {
8     public static String value = "";
9 }
 1 package com.qf.test10;
 2 
 3 import com.qf.test10.pojo.Entity;
 4 
 5 /**
 6  * @author qf
 7  * @create 2018-09-18 15:52
 8  * 生产者类
 9  */
10 public class Producer {
11     private String lock;
12 
13     public Producer(String lock) {
14         this.lock = lock;
15     }
16 
17     public void setValue(){
18         try {
19             synchronized (lock){
20                 if(!Entity.value.equals("")){
21                     lock.wait();
22                 }
23                 String value = System.currentTimeMillis()+"_"+System.nanoTime();
24                 System.out.println("set的值是"+value);
25                 Entity.value = value;
26                 lock.notify();
27             }
28         } catch (InterruptedException e) {
29             e.printStackTrace();
30         }
31     }
32 }
 1 package com.qf.test10;
 2 
 3 import com.qf.test10.pojo.Entity;
 4 
 5 /**
 6  * @author qf
 7  * @create 2018-09-18 15:52
 8  * 消费者类
 9  */
10 public class Consumer {
11     private String lock;
12 
13     public Consumer(String lock) {
14         this.lock = lock;
15     }
16 
17     public void getValue(){
18         try {
19             synchronized (lock){
20                 if(Entity.value.equals("")){
21                     lock.wait();
22                 }
23                 System.out.println("get的值"+Entity.value);
24                 Entity.value = "";
25                 lock.notify();
26             }
27         } catch (InterruptedException e) {
28             e.printStackTrace();
29         }
30     }
31 }

线程类

 1 package com.qf.test10.thread;
 2 
 3 import com.qf.test10.Producer;
 4 
 5 /**
 6  * @author qf
 7  * @create 2018-09-18 16:08
 8  */
 9 public class ThreadP extends Thread {
10     private Producer producer;
11 
12     public ThreadP(Producer producer) {
13         this.producer = producer;
14     }
15 
16     @Override
17     public void run() {
18         while(true) {
19             producer.setValue();
20         }
21     }
22 }
 1 package com.qf.test10.thread;
 2 
 3 import com.qf.test10.Consumer;
 4 
 5 /**
 6  * @author qf
 7  * @create 2018-09-18 16:11
 8  */
 9 public class ThreadC extends Thread {
10     private Consumer consumer;
11 
12     public ThreadC(Consumer consumer) {
13         this.consumer = consumer;
14     }
15 
16     @Override
17     public void run() {
18         while (true) {
19             consumer.getValue();
20         }
21     }
22 }

测试运行

 1 package com.qf.test10;
 2 
 3 import com.qf.test10.thread.ThreadC;
 4 import com.qf.test10.thread.ThreadP;
 5 
 6 /**
 7  * @author qf
 8  * @create 2018-09-18 16:12
 9  */
10 public class Run {
11     public static void main(String[] args) {
12         String lock = new String("");
13         Producer p = new Producer(lock);
14         Consumer c = new Consumer(lock);
15         ThreadP tp = new ThreadP(p);
16         tp.start();
17         ThreadC tc = new ThreadC(c);
18         tc.start();
19     }
20 }

打印输出

set的值是1537259244097_800479975994656
get的值1537259244097_800479975994656
set的值是1537259244097_800479976020503
get的值1537259244097_800479976020503
set的值是1537259244097_800479976042246
get的值1537259244097_800479976042246
set的值是1537259244097_800479976062349
get的值1537259244097_800479976062349
set的值是1537259244097_800479976083272
get的值1537259244097_800479976083272
set的值是1537259244097_800479976103785
get的值1537259244097_800479976103785
set的值是1537259244097_800479976124298
get的值1537259244097_800479976124298
set的值是1537259244097_800479976144400
get的值1537259244097_800479976144400
.............

如果以此为基础,设计多个生产者和多个消费者,那么运行过程中很可能会发生假死的情况,也就是所有线程都呈现等待的状态

多个生产者与多个消费者:操作值

修改Producer.java,Consumer.java以及测试类

 1 package com.qf.test10;
 2 
 3 import com.qf.test10.pojo.Entity;
 4 
 5 /**
 6  * @author qf
 7  * @create 2018-09-18 15:52
 8  * 生产者类
 9  */
10 public class Producer {
11     private String lock;
12 
13     public Producer(String lock) {
14         this.lock = lock;
15     }
16 
17     public void setValue(){
18         try {
19             synchronized (lock){
20                 while (!Entity.value.equals("")){
21                     System.out.println("生产者 "+Thread.currentThread().getName()+" WAITING了★");
22                     lock.wait();
23                 }
24                 System.out.println("生产者 "+Thread.currentThread().getName()+" RUNNABLE了");
25                 String value = System.currentTimeMillis()+"_"+System.nanoTime();
26                 //System.out.println("set的值是"+value);
27                 Entity.value = value;
28                 lock.notify();
29             }
30         } catch (InterruptedException e) {
31             e.printStackTrace();
32         }
33     }
34 }
 1 package com.qf.test10;
 2 
 3 import com.qf.test10.pojo.Entity;
 4 
 5 /**
 6  * @author qf
 7  * @create 2018-09-18 15:52
 8  * 消费者类
 9  */
10 public class Consumer {
11     private String lock;
12 
13     public Consumer(String lock) {
14         this.lock = lock;
15     }
16 
17     public void getValue(){
18         try {
19             synchronized (lock){
20                 if(Entity.value.equals("")){
21                     System.out.println("消费者 "+Thread.currentThread().getName()+" WAITING了☆");
22                     lock.wait();
23                 }
24                 System.out.println("消费者 "+Thread.currentThread().getName()+" RUNNABLE了");
25                 //System.out.println("get的值"+Entity.value);
26                 Entity.value = "";
27                 lock.notify();
28             }
29         } catch (InterruptedException e) {
30             e.printStackTrace();
31         }
32     }
33 }
 1 package com.qf.test10;
 2 
 3 import com.qf.test10.thread.ThreadC;
 4 import com.qf.test10.thread.ThreadP;
 5 
 6 /**
 7  * @author qf
 8  * @create 2018-09-18 16:12
 9  */
10 public class Run {
11     public static void main(String[] args) throws InterruptedException {
12         String lock = new String("");
13         Producer p = new Producer(lock);
14         Consumer c = new Consumer(lock);
15         /*ThreadP tp = new ThreadP(p);
16         tp.start();
17         ThreadC tc = new ThreadC(c);
18         tc.start();*/
19         ThreadP[] threadPS = new ThreadP[2];
20         ThreadC[] threadCS = new ThreadC[2];
21         for (int i = 0; i < 2; i++) {
22             threadPS[i] = new ThreadP(p);
23             threadPS[i].setName("生产者"+(i+1));
24             threadPS[i].start();
25             threadCS[i] = new ThreadC(c);
26             threadCS[i].setName("消费者"+(i+1));
27             threadCS[i].start();
28         }
29 
30         Thread.sleep(5000);
31         Thread[] threads = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
32         Thread.currentThread().getThreadGroup().enumerate(threads);
33         for (int i = 0; i < threads.length; i++) {
34             System.out.println(threads[i].getName()+" "+threads[i].getState());
35         }
36     }
37 }

打印结果

生产者 生产者1 RUNNABLE了
生产者 生产者1 WAITING了★
生产者 生产者2 WAITING了★
消费者 消费者1 RUNNABLE了
消费者 消费者1 WAITING了☆
生产者 生产者1 RUNNABLE了
生产者 生产者1 WAITING了★
生产者 生产者2 WAITING了★
消费者 消费者2 RUNNABLE了
消费者 消费者2 WAITING了☆
消费者 消费者1 RUNNABLE了
消费者 消费者1 WAITING了☆
生产者 生产者1 RUNNABLE了
生产者 生产者1 WAITING了★
生产者 生产者2 WAITING了★
main RUNNABLE
Monitor Ctrl-Break RUNNABLE
生产者1 WAITING
消费者1 WAITING
生产者2 WAITING
消费者2 WAITING

主要原因是因为notify可能唤醒的是同类(生产者唤醒生产者,消费者唤醒消费者)。最终导致所有线程都处于WAITING状态,程序进而呈现假死状态

只要将Producer和Consumer中的notify修改为notifyAll即可,这样就不至于出现假死状态

一个生产者和一个消费者:操作栈

 1 package com.qf.test11.pojo;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 /**
 7  * @author qf
 8  * @create 2018-09-18 17:14
 9  */
10 public class MyStack {
11     private List list = new ArrayList();
12     synchronized public void push(){
13         try {
14             if (list.size() == 1){
15                 this.wait();
16             }
17             list.add("test"+Math.random());
18             this.notify();
19             System.out.println("push = "+list.size());
20         } catch (InterruptedException e) {
21             e.printStackTrace();
22         }
23     }
24     public synchronized void pop(){
25         try {
26             if(list.size() == 0){
27                 //System.out.println("pop操作: "+Thread.currentThread().getName()+"线程wait状态");
28                 this.wait();
29             }
30             System.out.println("pop操作: "+Thread.currentThread().getName()+"线程,获取值="+list.get(0));
31             list.remove(0);
32             this.notify();
33             System.out.println("pop = "+list.size());
34         } catch (InterruptedException e) {
35             e.printStackTrace();
36         }
37     }
38 }

生产者/消费者

 1 package com.qf.test11;
 2 
 3 import com.qf.test11.pojo.MyStack;
 4 
 5 /**
 6  * @author qf
 7  * @create 2018-09-18 17:13
 8  * 生产者
 9  */
10 public class Producer {
11     private MyStack myStack;
12 
13     public Producer(MyStack myStack) {
14         this.myStack = myStack;
15     }
16 
17     public void pushService(){
18         myStack.push();
19     }
20 }
 1 package com.qf.test11;
 2 
 3 import com.qf.test11.pojo.MyStack;
 4 
 5 /**
 6  * @author qf
 7  * @create 2018-09-18 17:14
 8  */
 9 public class Consumer {
10     private MyStack myStack;
11 
12     public Consumer(MyStack myStack) {
13         this.myStack = myStack;
14     }
15     public void popService(){
16         myStack.pop();
17     }
18 }

线程类

 1 package com.qf.test11.thread;
 2 
 3 import com.qf.test11.Producer;
 4 
 5 /**
 6  * @author qf
 7  * @create 2018-09-18 17:13
 8  */
 9 public class ThreadP extends Thread {
10     private Producer producer;
11 
12     public ThreadP(Producer producer) {
13         this.producer = producer;
14     }
15 
16     @Override
17     public void run() {
18         while (true){
19             producer.pushService();
20         }
21     }
22 }
 1 package com.qf.test11.thread;
 2 
 3 import com.qf.test11.Consumer;
 4 
 5 /**
 6  * @author qf
 7  * @create 2018-09-18 17:14
 8  */
 9 public class ThreadC extends Thread {
10     private Consumer consumer;
11 
12     public ThreadC(Consumer consumer) {
13         this.consumer = consumer;
14     }
15 
16     @Override
17     public void run() {
18         while (true){
19             consumer.popService();
20         }
21     }
22 }

测试运行

 1 package com.qf.test11;
 2 
 3 import com.qf.test11.pojo.MyStack;
 4 import com.qf.test11.thread.ThreadC;
 5 import com.qf.test11.thread.ThreadP;
 6 
 7 /**
 8  * @author qf
 9  * @create 2018-09-18 17:34
10  */
11 public class Run {
12     public static void main(String[] args) {
13         MyStack myStack = new MyStack();
14         Producer p = new Producer(myStack);
15         Consumer c = new Consumer(myStack);
16         ThreadP tp = new ThreadP(p);
17         ThreadC tc = new ThreadC(c);
18         tp.setName("tp");
19         tc.setName("tc");
20         tp.start();
21         tc.start();
22     }
23 }

打印结果

push = 1
pop操作: tc线程,获取值=test0.8957260024057878
pop = 0
push = 1
pop操作: tc线程,获取值=test0.9236606274738514
pop = 0
push = 1
pop操作: tc线程,获取值=test0.7661156573296891
pop = 0
push = 1
pop操作: tc线程,获取值=test0.6523634151650343
pop = 0
push = 1
pop操作: tc线程,获取值=test0.08728918553111287
pop = 0
push = 1
pop操作: tc线程,获取值=test0.472483808512989
pop = 0
push = 1
pop操作: tc线程,获取值=test0.17456918848050884
pop = 0
push = 1
pop操作: tc线程,获取值=test0.1785536700399648
pop = 0
............

一个生产者与多个消费者:操作栈

 wait条件改变与假死

修改运行测试类:

 1 package com.qf.test11;
 2 
 3 import com.qf.test11.pojo.MyStack;
 4 import com.qf.test11.thread.ThreadC;
 5 import com.qf.test11.thread.ThreadP;
 6 
 7 /**
 8  * @author qf
 9  * @create 2018-09-19 9:41
10  */
11 public class Run2 {
12     public static void main(String[] args) {
13         MyStack stack = new MyStack();
14         // 一个生产者,五个消费者
15         Producer p = new Producer(stack);
16         Consumer c1 = new Consumer(stack);
17         Consumer c2 = new Consumer(stack);
18         Consumer c3 = new Consumer(stack);
19         Consumer c4 = new Consumer(stack);
20         Consumer c5 = new Consumer(stack);
21         // 一个生产者线程,五个消费者线程
22         ThreadP threadP = new ThreadP(p);
23         ThreadC threadC1 = new ThreadC(c1);
24         ThreadC threadC2 = new ThreadC(c2);
25         ThreadC threadC3 = new ThreadC(c3);
26         ThreadC threadC4 = new ThreadC(c4);
27         ThreadC threadC5 = new ThreadC(c5);
28 
29         // 启动线程
30         threadP.start();
31         threadC1.start();
32         threadC2.start();
33         threadC3.start();
34         threadC4.start();
35         threadC5.start();
36     }
37 }

将MyStack.java的pop方法中添加wait状态提醒

 1     public synchronized void pop(){
 2         try {
 3             if(list.size() == 0){
 4                 System.out.println("pop操作: "+Thread.currentThread().getName()+"线程wait状态");
 5                 this.wait();
 6             }
 7             System.out.println("pop操作: "+Thread.currentThread().getName()+"线程,获取值="+list.get(0));
 8             list.remove(0);
 9             this.notify();
10             System.out.println("pop = "+list.size());
11         } catch (InterruptedException e) {
12             e.printStackTrace();
13         }
14     }

打印结果

push = 1
push操作: Thread-0线程wait状态
pop操作: Thread-5线程,获取值=test0.47799193642831905
pop = 0
pop操作: Thread-5线程wait状态
pop操作: Thread-4线程wait状态
pop操作: Thread-3线程wait状态
pop操作: Thread-2线程wait状态
pop操作: Thread-1线程wait状态
push = 1
push操作: Thread-0线程wait状态
pop操作: Thread-5线程,获取值=test0.3154893136449711
pop = 0
pop操作: Thread-5线程wait状态
Exception in thread "Thread-4" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
    at java.util.ArrayList.rangeCheck(ArrayList.java:657)
    at java.util.ArrayList.get(ArrayList.java:433)
    at com.qf.test11.pojo.MyStack.pop(MyStack.java:31)
    at com.qf.test11.Consumer.popService(Consumer.java:16)
    at com.qf.test11.thread.ThreadC.run(ThreadC.java:19)

分析:

  假死:是因为消费者线程notify唤醒的也可能是消费者线程。notify修改为notifyAll

  异常:是因为MyStack.java类的if中条件改变后,并没有得到及时的响应导致多个wait状态的线程被唤醒,然后在执行remove时就会抛出异常。将Mystack.java中的if改为while即可

 1 package com.qf.test11.pojo;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 /**
 7  * @author qf
 8  * @create 2018-09-18 17:14
 9  */
10 public class MyStack {
11     private List list = new ArrayList();
12     synchronized public void push(){
13         try {
14             while (list.size() == 1){
15                 System.out.println("push操作: "+Thread.currentThread().getName()+"线程wait状态");
16                 this.wait();
17             }
18             list.add("test"+Math.random());
19             this.notifyAll();
20             System.out.println("push = "+list.size());
21         } catch (InterruptedException e) {
22             e.printStackTrace();
23         }
24     }
25     public synchronized void pop(){
26         try {
27             while(list.size() == 0){
28                 System.out.println("pop操作: "+Thread.currentThread().getName()+"线程wait状态");
29                 this.wait();
30             }
31             System.out.println("pop操作: "+Thread.currentThread().getName()+"线程,获取值="+list.get(0));
32             list.remove(0);
33             this.notifyAll();
34             System.out.println("pop = "+list.size());
35         } catch (InterruptedException e) {
36             e.printStackTrace();
37         }
38     }
39 }

打印结果

push = 1
push操作: Thread-0线程wait状态
pop操作: Thread-3线程,获取值=test0.6386092869303013
pop = 0
pop操作: Thread-3线程wait状态
pop操作: Thread-5线程wait状态
pop操作: Thread-2线程wait状态
pop操作: Thread-1线程wait状态
pop操作: Thread-4线程wait状态
push = 1
push操作: Thread-0线程wait状态
pop操作: Thread-4线程,获取值=test0.18590994690226936
pop = 0
pop操作: Thread-4线程wait状态
pop操作: Thread-1线程wait状态
pop操作: Thread-2线程wait状态
pop操作: Thread-5线程wait状态
pop操作: Thread-3线程wait状态
push = 1
................

多个生产者与一个消费者:操作栈

继续使用上面一个生产者和多个消费者的代码,只需要修改运行测试类即可:

 1 package com.qf.test11;
 2 
 3 import com.qf.test11.pojo.MyStack;
 4 import com.qf.test11.thread.ThreadC;
 5 import com.qf.test11.thread.ThreadP;
 6 
 7 /**
 8  * @author qf
 9  * @create 2018-09-19 14:16
10  */
11 public class Run3 {
12     public static void main(String[] args) {
13         MyStack stack = new MyStack();
14         Producer p1 = new Producer(stack);
15         Producer p2 = new Producer(stack);
16         Producer p3 = new Producer(stack);
17         Producer p4 = new Producer(stack);
18         Producer p5 = new Producer(stack);
19         Consumer c = new Consumer(stack);
20 
21         ThreadP threadP1 = new ThreadP(p1);
22         ThreadP threadP2 = new ThreadP(p2);
23         ThreadP threadP3 = new ThreadP(p3);
24         ThreadP threadP4 = new ThreadP(p4);
25         ThreadP threadP5 = new ThreadP(p5);
26         ThreadC threadC = new ThreadC(c);
27 
28         threadP1.start();
29         threadP2.start();
30         threadP3.start();
31         threadP4.start();
32         threadP5.start();
33         threadC.start();
34 
35     }
36 }

打印结果

push = 1
push操作: Thread-0线程wait状态
push操作: Thread-4线程wait状态
push操作: Thread-1线程wait状态
push操作: Thread-2线程wait状态
pop操作: Thread-5线程,获取值=test0.05326156970977369
pop = 0
push = 1
push操作: Thread-3线程wait状态
push操作: Thread-2线程wait状态
pop操作: Thread-5线程,获取值=test0.3953490110525745
pop = 0
pop操作: Thread-5线程wait状态
push = 1
push操作: Thread-1线程wait状态
push操作: Thread-4线程wait状态
push操作: Thread-0线程wait状态
pop操作: Thread-5线程,获取值=test0.8982622387643299
pop = 0
..........

多个生产者和多个消费者:操作栈

继续使用上面一个生产者和多个消费者的代码,只需要修改运行测试类即可:

 1 package com.qf.test11;
 2 
 3 import com.qf.test11.pojo.MyStack;
 4 import com.qf.test11.thread.ThreadC;
 5 import com.qf.test11.thread.ThreadP;
 6 
 7 /**
 8  * @author qf
 9  * @create 2018-09-19 14:51
10  *  多个生产者和多个消费者
11  */
12 public class Run4 {
13     public static void main(String[] args) {
14         MyStack stack = new MyStack();
15         Producer p1 = new Producer(stack);
16         Producer p2 = new Producer(stack);
17         Producer p3 = new Producer(stack);
18         Producer p4 = new Producer(stack);
19         Producer p5 = new Producer(stack);
20         Consumer c1 = new Consumer(stack);
21         Consumer c2 = new Consumer(stack);
22         Consumer c3 = new Consumer(stack);
23         Consumer c4 = new Consumer(stack);
24         Consumer c5 = new Consumer(stack);
25 
26         ThreadP threadP1 = new ThreadP(p1);
27         ThreadP threadP2 = new ThreadP(p2);
28         ThreadP threadP3 = new ThreadP(p3);
29         ThreadP threadP4 = new ThreadP(p4);
30         ThreadP threadP5 = new ThreadP(p5);
31         ThreadC threadC1 = new ThreadC(c1);
32         ThreadC threadC2 = new ThreadC(c2);
33         ThreadC threadC3 = new ThreadC(c3);
34         ThreadC threadC4 = new ThreadC(c4);
35         ThreadC threadC5 = new ThreadC(c5);
36 
37         threadP1.start();
38         threadP2.start();
39         threadP3.start();
40         threadP4.start();
41         threadP5.start();
42         threadC1.start();
43         threadC2.start();
44         threadC3.start();
45         threadC4.start();
46         threadC5.start();
47     }
48 }

打印结果

push = 1
push操作: Thread-3线程wait状态
pop操作: Thread-9线程,获取值=test0.02917777060692428
pop = 0
pop操作: Thread-9线程wait状态
push = 1
push操作: Thread-4线程wait状态
push操作: Thread-1线程wait状态
pop操作: Thread-9线程,获取值=test0.8633026873071624
pop = 0
pop操作: Thread-9线程wait状态
push = 1
push操作: Thread-3线程wait状态
pop操作: Thread-5线程,获取值=test0.6089625139678617
pop = 0
pop操作: Thread-5线程wait状态
push = 1
.................

 

posted @ 2018-09-18 17:45  *青锋*  阅读(450)  评论(0编辑  收藏  举报