多线程学习之六生产消费者模式

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;
    }
}
View Code

生产蛋糕类

/**
 * 
 */
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();
                }
            }
            
//        }
    }

}
View Code

消费蛋糕类

/**
 * 
 */
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();
            }
            
        }
    }

}
View Code

主线程:

/**
 * 
 */
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();
    }
}
View Code

测试结果

线程【黄师傅】正在存放蛋糕【黄师傅de蛋糕0】
尚顾客吃了蛋糕【黄师傅de蛋糕0】
线程【尚师傅】正在存放蛋糕【尚师傅de蛋糕0】
刘顾客吃了蛋糕【尚师傅de蛋糕0】
线程【刘师傅】正在存放蛋糕【刘师傅de蛋糕0】
黄顾客吃了蛋糕【刘师傅de蛋糕0】
线程【黄师傅】正在存放蛋糕【黄师傅de蛋糕1】
线程【刘师傅】正在存放蛋糕【刘师傅de蛋糕1】
黄顾客吃了蛋糕【黄师傅de蛋糕1】
刘顾客吃了蛋糕【刘师傅de蛋糕1】
原文地址:https://www.cnblogs.com/quchengfeng/p/4904855.html