多线程学习之六生产消费者模式
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案例
三个生产蛋糕的线程,三个消费蛋糕的线程,一个传递蛋糕的桌子。
传递蛋糕的桌子
/** * */ package com.benxq.thread7; /** * 桌子类 * Created by qucf on 2015年10月23日. */ public class Table { //存放蛋糕的数组 private String[] cakes; //下一个坊蛋糕的位置 private int nextPut; //下一个取蛋糕的位置 private int nextGet; //桌子上蛋糕的数量 private int count; public Table(int count) { this.cakes=new String[count]; this.nextGet=0; this.nextPut=0; } //存放蛋糕 public synchronized void putCake(String cake) throws InterruptedException{ System.out.println("线程【"+Thread.currentThread().getName()+"】正在存放蛋糕【"+cake+"】"); //警戒:如果桌子上的蛋糕吃的慢了 就阻塞线程 if(count>=cakes.length){ wait(); } //将蛋糕放入模拟队列中 cakes[nextPut]=cake; //计算出下一个蛋糕的位置 nextPut=(nextPut+1)%cakes.length; count++; //唤醒其他线程 notifyAll(); } //取蛋糕 public synchronized String getCake() throws InterruptedException{ // if(count<=0){ wait(); } String cake=cakes[nextGet]; nextGet=(nextGet+1)%cakes.length; count--; notifyAll(); System.out.println(""+Thread.currentThread().getName()+"吃了蛋糕【"+cake+"】"); return cake; } }
生产蛋糕类
/** * */ package com.benxq.thread7; /** * 生成蛋糕的线程 * Created by qucf on 2015年10月23日. */ public class PutCakeThread implements Runnable{ private Table table; public PutCakeThread(Table table) { this.table=table; } @Override public void run() { // while(true){ for (int i = 0; i < 10; i++) { try { table.putCake(Thread.currentThread().getName()+"de蛋糕"+i); Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // } } }
消费蛋糕类
/** * */ package com.benxq.thread7; /** * 吃蛋糕线程 * Created by qucf on 2015年10月23日. */ public class GetCakeThread implements Runnable{ private Table table; public GetCakeThread(Table table) { this.table=table; } @Override public void run() { while(true){ try { String cake=table.getCake(); Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
主线程:
/** * */ package com.benxq.thread7; /** * 主线程 * Created by qucf on 2015年10月23日. */ public class Test { public static void main(String[] args) { Table table=new Table(5); Thread put1=new Thread(new PutCakeThread(table)); put1.setName("黄师傅"); Thread put2=new Thread(new PutCakeThread(table)); put2.setName("尚师傅"); Thread put3=new Thread(new PutCakeThread(table)); put3.setName("刘师傅"); Thread get1=new Thread(new GetCakeThread(table)); get1.setName("尚顾客"); Thread get2=new Thread(new GetCakeThread(table)); get2.setName("刘顾客"); Thread get3=new Thread(new GetCakeThread(table)); get3.setName("黄顾客"); put1.start(); put2.start(); put3.start(); get1.start(); get2.start(); get3.start(); } }
测试结果
线程【黄师傅】正在存放蛋糕【黄师傅de蛋糕0】
尚顾客吃了蛋糕【黄师傅de蛋糕0】
线程【尚师傅】正在存放蛋糕【尚师傅de蛋糕0】
刘顾客吃了蛋糕【尚师傅de蛋糕0】
线程【刘师傅】正在存放蛋糕【刘师傅de蛋糕0】
黄顾客吃了蛋糕【刘师傅de蛋糕0】
线程【黄师傅】正在存放蛋糕【黄师傅de蛋糕1】
线程【刘师傅】正在存放蛋糕【刘师傅de蛋糕1】
黄顾客吃了蛋糕【黄师傅de蛋糕1】
刘顾客吃了蛋糕【刘师傅de蛋糕1】