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


    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】
  • 相关阅读:
    GitHub超详细图文攻略
    HTML5本地存储——IndexedDB二:索引
    HTML5 indexedDb 数据库
    js 对象 浅拷贝 和 深拷贝
    《黑客大曝光》实践部分——sql注入(7/8)
    Linux内核设计第五周——扒开系统调用三层皮(下)
    读书笔记——《黑客大曝光》(6/8)
    《linux内核设计与实现》读书笔记第五章——系统调用
    Linux内核设计第四周——扒开系统调用三层皮
    《linux内核设计与实现》读书笔记第一、二章
  • 原文地址:https://www.cnblogs.com/quchengfeng/p/4904855.html
Copyright © 2020-2023  润新知