zoukankan      html  css  js  c++  java
  • 多线程程序设计学习(6)Producer-Consumer模式

    Producer-Consumer【生产消费者模式】
    一:Producer-Consumer pattern的参与者
    --->产品(蛋糕)
    --->通道(传递蛋糕的桌子)
    --->生产者线程(制造蛋糕的线程)
    --->消费者线程(吃掉蛋糕的线程)


           

    二:Producer-Consumer pattern模式什么时候使用
    --->大量生产+大量消费的模式


    三:Producer-Consumer pattern思考
    --->【生产消费者模式】,肩负保护数据安全性使命的是通道参与者。通道参与者参与进行线程间的共享互斥,让生产着能正确将数据传递到消费者手中。
    --->通道(桌子)的put和take方法都使用了【独木桥模式】,而生产者和消费者都不想依赖table类的详细实现,也就说,生产者不必理会其他线程,只管生产并put,同样消费者也不必理会其他线程,只管take就好。而线程的共享互斥,synchronized,wati,notifyAll这些考虑的多线程操作的代码,全都隐藏在通道table类里。提高了table的复用性。

    --->生产者消费者模式,存在两方处理速率不同的话,必然造成一方等待,或占用通道大量内存的问题。


    --->多线程合作的口决
            线程的合作要想:放在中间的东西
            线程的互斥要想:应该保护的东西

    --->多生产者对单一消费者,如果情况合理,一方可以不用考虑互斥,就不用加锁,提升性能。

    四进阶说明
    --->习惯编写java多线程。当所调用的方法抛出,或内部抓住异常:InterruptedException.
            ==>通常传递给我们两个信息。(1)这是‘需要花点时间’的方法(2)这是‘可以取消’的方法
            ==>告诉我们方法内部有这三个选手:java.lang.Object类里的wait方法        
                                                                                         java.lang.Thread类里的sleep方法
                                                                                         java.lang.Thread类里的join方法
                    
    --->需要花点时间的方法
            wait==>执行wait方法的线程,进入wait set里休眠,等待notify,notifyAll唤醒。在休眠期间不会活动,因此需要花费时间。
            sleep==>执行sleep,会暂停执行参数内所设置的时间,这也是需要花费时间
            join==>会等待制定的线程结束为止。才执行本线程。也就是花费直到制定线程结束之前的这段时间

    --->可以取消的方法。
            因为需要花费时间,会降低程序的响应性,所以我们会希望像下面这样可以在中途放弃(取消)执行这个方法
            1取消wait方法等待notify,notifyAll唤醒的操作
            2取消sleep方法等待设置长度时间的操作
            3取消join方法等待其他线程结束的操作

    --->取消线程等待的详细解说
           (1) A线程的实例为athread,线程体内部执行Thread.sleep(1000)时,想取消其睡眠状态。则需要B线程中取消。
                    --->在B线程中用athread.interrupt().
                    --->则A线程终止睡眠,并抛出或被抓住InterruptedException
                    --->这个时候A线程的catch块的代码,至关重要。
          (2) A线程的实例为athread,线程体内部执行wait()时,想取消等待notify,notifyAll唤醒的操作。则需要B线程中取消。
                    --->在B线程体中用athread.interrupt().
                    --->则A线程终止等待状态,并尝试重新获取锁定。
                    --->获取锁定后,抛出或被抓住InterruptedException
                    --->这个时候A线程的catch块的代码,至关重要。
          (3)A线程的实例为athread,线程体内部执行join(),想等待其他线程执行结束。则需要B线程中取消。
                     --->在B线程中用athread.interrupt().
                    --->则A线程终止睡眠,并抛出或被抓住InterruptedException
                    --->这个时候A线程的catch块的代码,至关重要。


    --->线程对象.interrupt(),Thead.interrupted,线程对象.isinterrupted()区别
            ==>interrupt()让等待或休眠的线程变成中断状态,抛出异常
            ==>interrupted()检查线程的中断状态
                                            是中断状态,返回true,并将中断状态修改成非中断状态
                                            不是中断状态,返回false,不做任何操作
            ==>isinterrupted简单的检查线程是否为中断状态,是返回true,不是返回false,不做任何操作



         Producer-Consumer案例
            三个生产蛋糕的线程,三个消费蛋糕的线程,一个传递蛋糕的桌子。

    传递蛋糕的桌子

     1 package com.yeepay.sxf.thread5;
     2 /**
     3  * 在消费线程和生产线程中间起传递作用的桌子
     4  * @author sxf
     5  *
     6  */
     7 
     8 public class Table {
     9     //存放蛋糕的数组
    10     private String[] cakes;
    11     //下一个放蛋糕的位置
    12     private int nextPut;
    13     //下一个取蛋糕的位置
    14     private int nextGet;
    15     //蛋糕数组中蛋糕的数量
    16     private int count;
    17     //构造器
    18     public Table (int count){
    19         this.cakes=new String[count];
    20         this.nextGet=0;
    21         this.nextPut=0;
    22     }
    23     //存放蛋糕
    24     public synchronized  void putCakes(String cake) throws InterruptedException{
    25         System.out.println("["+Thread.currentThread().getName()+"]put"+cake);
    26         //警戒条件  如果桌子上蛋糕,慢了,就阻塞生产线程。
    27         while (count>=cakes.length) {
    28             wait();            
    29         }
    30         //将蛋糕放入模拟队列
    31         cakes[nextPut]=cake;
    32         //算出下一个放蛋糕的位置
    33         nextPut=(nextPut+1)%cakes.length;
    34         //蛋糕数据量加1
    35         count++;
    36         //唤醒别的线程
    37         notifyAll();
    38     }
    39     
    40     //取蛋糕
    41     public synchronized String takeCake() throws InterruptedException{
    42         //判断桌子上是否有蛋糕,如果没有,阻塞线程
    43         while (count<=0) {
    44             wait();
    45         }
    46         //取出蛋糕
    47         String cake=cakes[nextGet];
    48         //计算出下一个取蛋糕的位置
    49         nextGet=(nextGet+1)%cakes.length;
    50         //蛋糕数量减一
    51         count--;
    52         //唤醒其他线程
    53         notifyAll();
    54         System.out.println("【"+Thread.currentThread().getName()+"】get"+cake);
    55         return cake;
    56     }
    57 }
    View Code

    生产蛋糕的线程

     1 package com.yeepay.sxf.thread5;
     2 /**
     3  * 制造蛋糕线程
     4  * @author sxf
     5  *
     6  */
     7 public class MakeCakeThread implements Runnable{
     8     //存放蛋糕的桌子
     9     private Table table;
    10     
    11     //构造器
    12     public  MakeCakeThread(Table table) {
    13         this.table=table;
    14     }
    15 
    16     @Override
    17     public void run() {
    18         while (true) {
    19             for (int i = 0; i <100; i++) {
    20                 
    21                 try {
    22                     //生产蛋糕,并放入
    23                     table.putCakes(Thread.currentThread().getName()+"的蛋糕"+i);
    24                     //当前线程休息1秒钟
    25                 
    26                     Thread.sleep(1000);
    27                 } catch (InterruptedException e) {
    28                     // TODO Auto-generated catch block
    29                     e.printStackTrace();
    30                 }
    31             }
    32             
    33         }
    34         
    35     }
    36 
    37     
    38 }
    View Code

    吃掉蛋糕的线程

     1 package com.yeepay.sxf.thread5;
     2 /**
     3  * 吃蛋糕的线程
     4  * @author sxf
     5  *
     6  */
     7 public class EatCakeThread implements Runnable {
     8     //桌子
     9     private  Table table;
    10     //构造器
    11     public EatCakeThread(Table table){
    12         this.table=table;
    13     }
    14     /**
    15      * 线程体
    16      */
    17     @Override
    18     public void run() {
    19         while(true){
    20             try {
    21                 String cake=table.takeCake();
    22                 Thread.sleep(500);
    23             } catch (InterruptedException e) {
    24                 // TODO Auto-generated catch block
    25                 e.printStackTrace();
    26             }
    27         }
    28         
    29     }
    30     
    31 }
    View Code

    测试类

     1 package com.yeepay.sxf.thread5;
     2 /**
     3  * 测试类
     4  * @author sxf
     5  *
     6  */
     7 public class Test {
     8 
     9     public static void main(String[] args) {
    10         //声明一张桌子
    11         Table table=new Table(4);
    12         //声明生产蛋糕的线程
    13         Thread makeThread1=new Thread(new MakeCakeThread(table));
    14         makeThread1.setName("尚晓飞师傅");
    15         Thread makeThread2=new Thread(new MakeCakeThread(table));
    16         makeThread2.setName("范林军师傅");
    17         Thread makeThread3=new Thread(new MakeCakeThread(table));
    18         makeThread3.setName("黄栓林师傅");
    19         
    20         //声明吃蛋糕的线程
    21         Thread eatThread1=new Thread(new EatCakeThread(table));
    22         eatThread1.setName("顾客1");
    23         Thread eatThread2=new Thread(new EatCakeThread(table));
    24         eatThread2.setName("顾客2");
    25         Thread eatThread3=new Thread(new EatCakeThread(table));
    26         eatThread3.setName("顾客3");
    27         
    28         //启动线程
    29         makeThread1.start();
    30         makeThread2.start();
    31         makeThread3.start();
    32         eatThread1.start();
    33         eatThread2.start();
    34         eatThread3.start();
    35         
    36         
    37     }
    38 }
    View Code

    测试结果:

    [尚晓飞师傅]put尚晓飞师傅的蛋糕0
    [范林军师傅]put范林军师傅的蛋糕0
    【顾客1】get尚晓飞师傅的蛋糕0
    【顾客2】get范林军师傅的蛋糕0
    [黄栓林师傅]put黄栓林师傅的蛋糕0
    【顾客3】get黄栓林师傅的蛋糕0
    [尚晓飞师傅]put尚晓飞师傅的蛋糕1
    【顾客3】get尚晓飞师傅的蛋糕1
    [范林军师傅]put范林军师傅的蛋糕1
    【顾客1】get范林军师傅的蛋糕1
    [黄栓林师傅]put黄栓林师傅的蛋糕1
    【顾客2】get黄栓林师傅的蛋糕1
    [尚晓飞师傅]put尚晓飞师傅的蛋糕2

  • 相关阅读:
    在django中用MySQL为数据库 新建一个项目的流程
    django ORM中的RelatedManager(关联管理器)
    URL的命名和反向解析
    自定义分页的插件
    从数据库读出数据分页显示
    往数据库批量插入试验数据
    JDK9对集合添加的优化
    全栈工程师
    List的三个子类的特点
    List集合
  • 原文地址:https://www.cnblogs.com/shangxiaofei/p/4658926.html
Copyright © 2011-2022 走看看