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


    参考书籍《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
    .................
  • 相关阅读:
    使用Jquery 来AJAX操作!
    血细胞形态异常的临床意义
    骨髓细胞检查
    保护眼睛的颜色
    php的一些技巧
    播放ASF文件无声音
    不被其他网站载入框架之内
    如何判断一个页面加载所耗费的时间
    教大家如何洗葡萄,很好吃哦!
    急性粒细胞白血病部分分化型AMLM2
  • 原文地址:https://www.cnblogs.com/qf123/p/9670370.html
Copyright © 2020-2023  润新知