多线程程序设计学习(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 }

生产蛋糕的线程

 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 }

吃掉蛋糕的线程

 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 }

测试类

 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 }

测试结果:

[尚晓飞师傅]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

时间: 2024-11-05 13:33:45

多线程程序设计学习(6)Producer-Consumer模式的相关文章

多线程程序设计学习(2)之single threaded execution pattern

Single Threaded Execution Pattern[独木桥模式] 一:single threaded execution pattern的参与者--->SharedResource(共享资源) 二:single threaded execution pattern模式什么时候使用--->多线程程序设计时--->数据可被多个线程访问的时候--->共享资源状态可能变化的时候--->需要确保数据安全性的时候 三:single threaded execution p

多线程程序设计学习(3)immutable pattern模式

Immutable pattern[坚不可摧模式] 一:immutable pattern的参与者--->immutable(不变的)参与者        1.1:immutable参与者是一个字段的值都无法更改的类.        1.2:immutable也没有任何用来更改字段值的方法.        1.3:immutable参与者方法不需要设置synchronized 二:immutable pattern模式什么时候使用--->当实例产生后,状态不再变化时        2.1实例状

多线程程序设计学习(5)balking模式和timed模式

Balking[返回模式]一:balking pattern的参与者--->GuardedObject(被警戒的对象) --->该模式的角色:模拟修改警戒对象的线程,当警戒条件达到执行具体操作的线程,参与者(被警戒的参与者) 二:balking pattern模式什么时候使用--->不需要刻意去执行什么操作的时候(比如说自动保存)--->不想等待警戒条件成立时.(不让线程休息)--->警戒条件只有第一次成立时候. 三:balking pattern思考--->balki

多线程程序设计学习(9)worker pattern模式

Worker pattern[工作模式]一:Worker pattern的参与者--->Client(委托人线程)--->Channel(通道,里边有,存放请求的队列)--->Request(工作内容的包装)--->Worker(工人线程) 二:Worker pattern模式什么时候使用--->类似生产者消费者 三:Worker pattern思考 四进阶说明--->工作线程取出请求内容包装后,根据多态,不同的请求执行不同的业务方法 Request接口 1 packa

多线程程序设计学习(11)Two-phapse-Termination pattern

Two-phapse-Termination[A终止B线程] 一:Two-phapse-Termination的参与者--->A线程--->B线程 二:Two-phapse-Termination模式什么时候使用--->当A线程需要B线程终止时 三:Two-phapse-Termination思考       ---> 优雅的终止线程        (1)安全地终止(安全性)==>即使收到终止请求,也不会马上结束线程,而是表示收到终止请求的标识.以对象不被损坏为前提,安全终止

多线程程序设计学习(7)read-write lock pattern

Read-Write Lock Pattern[读写]一:Read-Write Lock Pattern的参与者--->读写锁--->数据(共享资源)--->读线程--->写线程 二Read-Write Lock Pattern模式什么时候使用---> * 为了多线线程环境下保护数据安全,我们必须避免的冲突 * 一个线程读取,另一个线程写入的read-write conflick * 一个线程写入,另一个线程写入的write-write conflick * 一个线程读取,另

多线程程序设计学习(8)Thread-Per-Message

Thread-Per-Message[这个工作交给你模式]一:Thread-Per-Message的参与者--->Client(委托人)--->host(中介开线程)--->hepler(真正工作的工人) 二:Thread-Per-Message模式什么时候使用--->提升响应时间,降低延迟时间--->适合在操作顺序无所谓的时候--->(委托人)不需要返回值的时候--->应用在服务器的制作(我提供原材料,你加工,我不关心结果)--->调用方法+启动线程---

多线程程序设计学习(1)之多线程程序的评量标准

多线程程序的评量标准--->安全性[不损坏对象]        (1)不损坏对象是一种比喻手法,就是数据的一致性.比如银行的扣款操作,并发扣款,账户不会出现负数的余额.--->生存性[进行必要的处理]        (1)指也许不是现在,但一定会进行的必要处理.好比异常处理机制.在出现多线死锁等情况下的必要处理.--->复用性        (1)指可再利用的类,这不是必要条件,但却是体现程序质量重点条件.封装的意思.        (2)如果A类是一个已经确定可正常使用的软件程序的一部

多线程程序设计学习(13)Active Object pattern

Active Object[接收异步消息的对象] 一:Active Object的参与者--->客户端线程(发起某种操作请求处理)--->代理角色(工头)--->实际执行者(工人)--->主动对象接口(工人和工头)--->生产端线程(加工产品的线程)--->存放生产请求的队列(存放请求的队列)--->请求实例化(将方法的启动和执行分离的实例化包含)--->订单--->产品--->订单产品的共同接口 二:Active Object模式什么时候使用-