Java程序性能优化 读书笔记(十四)并行设计模式:生产者-消费者模式

转载:java线程深度解析(五)——并发模型(生产者-消费者)

转载:java 多线程并发系列之 生产者消费者模式的两种实现

一、生产者-消费者模式

在经典的多线程模式中,生产者-消费者为多线程间协作提供了良好的解决方案。基本原理是两类线程,即若干个生产者和若干个消费者,生产者负责提交用户请求任务(到内存缓冲区),消费者线程负责处理任务(从内存缓冲区中取任务进行处理),两类线程之间通过共享内存缓冲区进行通信。
共享内存缓冲区的存在避免生产者和消费者直接通信,且允许消费者和生产者执行速度上存在时间差,无论谁快谁慢,都可以通过缓冲区缓解,确保系统正常运行。

生产者-消费者模式的优点:

1. 它简化的开发,你可以独立地或并发的编写消费者和生产者,它仅仅只需知道共享对象是谁
2. 生产者不需要知道谁是消费者或者有多少消费者,对消费者来说也是一样
3. 生产者和消费者可以以不同的速度执行
4. 分离的消费者和生产者在功能上能写出更简洁、可读、易维护的代码

二、生产者-消费者模式的结构

生产者消费者模式中主要角色
生产者:提交用户请求,提取用户任务,并装入内存缓冲区;
消费者:在内存缓冲区中提取并处理任务;
内存缓冲区:缓存生产者提交的任务或数据,供消费者使用;
任务:生产者向内存缓冲区提交的数据结构;
Main:即Client客户端,使用生产者和消费者的客户端。

三、生产者消费者模式的实现

3.1 wait/notify方式

(1) Producer负责提交请求

public class Producer implements Runnable {
    PublicQueue t;

    public Producer() {

    }

    public Producer(PublicQueue t) {
        this.t = t;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                t.produce("test" + i);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

(2)Cusnumer负责处理请求

public class Consumer implements Runnable {
    PublicQueue queue;

    public Consumer() {
        // TODO Auto-generated constructor stub
    }

    public Consumer(PublicQueue obj) {
        this.queue = obj;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                queue.consume();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

(3) PublicQueue负责共享

public class PublicQueue {
    public static Object signal = new Object();
    boolean bFull = false;
    private List thingsList = new ArrayList();

    public void produce(String thing) throws Exception {
        synchronized (signal) {
            if (!bFull) {
                bFull = true;
                System.out.println("produce");
                thingsList.add(thing);
                signal.notify(); // 然后通知消费者
            }
        }
    }

    public String consume() {
        synchronized (signal) {
            if (!bFull) {
                try {
                    signal.wait(); // 队列为空。等待.....
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } // 进入signal待召队列,等待生产者的通知
            }
            bFull = false;
            System.out.println("consume"); // 读取buf 共享资源里面的东西
            signal.notify(); // 然后通知生产者
        }
        String result = "";
        if (thingsList.size() > 0) {
            result = thingsList.get(thingsList.size() - 1).toString();
            thingsList.remove(thingsList.size() - 1);
        }
        return result;
    }
}

(4) 主程序使用生产者和消费者

public class TestDemo {  
    public static void main(String []args)
    {
         PublicQueue queue= new PublicQueue();
         Consumer con= new Consumer(queue);
         Producer pro= new Producer(queue);

         Thread t1= new Thread(con);
         Thread t2= new Thread(pro);

         t1.start();
         t2.start();
    }
}

3.2 阻塞队列方式

阻塞队列实现生产者消费者模式超级简单,它提供开箱即用支持阻塞的方法put()和take(),开发者不需要写困惑的wait-nofity代码去实现通信。BlockingQueue 一个接口,Java5提供了不同的现实,如ArrayBlockingQueue和LinkedBlockingQueue,两者都是先进先出(FIFO)顺序。而ArrayLinkedQueue是自然有界的,LinkedBlockingQueue可选的边界。下面这是一个完整的生产者消费者代码例子,对比传统的wait、nofity代码,它更易于理解。

实现采用BlockingQueue充当缓冲区,创建一个任务类PCData,生产者负责创建PCData对象放入缓冲区,消费者负责处理从缓冲区中取出PCData对象进行处理。

(1)Producer生产者线程

/*  
 * 负责创建数据对象PCD并提交到内存缓冲区中  
 */  
public class Producer implements Runnable {  
    private volatile boolean isRunning=true;  
    private BlockingQueue<PCData> queue;  
    private static AtomicInteger count=new AtomicInteger();//总数,原子操作      
    private static final int SLEEPTIME=1000;      
    public  Producer(BlockingQueue<PCData> queue){  
        this.queue=queue;  
    }  
      
    @Override  
    public void run() {  
        PCData data=null;  
        Random r=new Random();  
        System.out.println("生产者当前线程"+Thread.currentThread().getId());  
          
        try{  
            while(isRunning)  
            {  
                Thread.sleep(r.nextInt(SLEEPTIME));  
                data=new PCData(count.incrementAndGet());//构造任务数据  
              
                System.out.println(count.incrementAndGet());  
                System.out.println(data +"已进入缓存区");  
                if(!queue.offer(data,2,TimeUnit.SECONDS))  
                {  
                    //提交数据到缓冲区  
                    System.err.println(data+"存入失败");  
                }  
            }  
        }catch(Exception e)  
        {  
            Thread.currentThread().interrupt();  
        }         
    }  
    public void stop()  
    {  
        isRunning=false;  
    }      
}  

(2)Consumer消费者线程:

/*  
 * 从缓冲区中获取PCData对象  
 */  
public class Consumer implements Runnable{  
    private BlockingQueue<PCData> queue;  
    private static final int SLEEPTIME=1000;  
      
    public Consumer(BlockingQueue<PCData> queue)  
    {  
        this.queue=queue;  
    }  
      
    public void run()  
    {  
        System.out.println("消费者开始取数据,当前线程ID:"+Thread.currentThread().getId());  
        Random r=new Random();  
        try  
        {  
            while(true)  
            {  
                PCData data=queue.take();  
                if(null!=data)  
                {  
                    System.out.println("从缓冲区中获取数据"+data.getData());  
                    int re=data.getData()*data.getData();//计算平方  
                    System.out.println(MessageFormat.format("{0}*{1}={2}",data.getData(),data.getData(),re));  
                    System.out.println("本数据对象处理完毕");  
                    Thread.sleep(r.nextInt(SLEEPTIME));  
                }  
                          
            }  
        }catch(Exception e)  
        {  
            Thread.currentThread().interrupt();  
        }  
    }  
}  
(3)PCData共享数据模型:

public final class PCData {  
    private final int intData;  
    public PCData(int d)  
    {  
        intData=d;  
    }  
    public PCData(String d)  
    {  
        intData=Integer.valueOf(d);  
    }  
      
    public int getData()  
    {  
        return intData;  
    }  
      
    @Override  
    public String toString()  
    {  
        return "data:"+intData;  
    }     
}  

(4)Main函数:

public class Client {  
    public static void main(String[] args) throws InterruptedException {  
        //建立缓冲区  
        BlockingQueue<PCData> queue=new LinkedBlockingDeque<PCData>(10);  
        //建立3个生产者  
        Producer p1=new Producer(queue);  
        Producer p2=new Producer(queue);  
        Producer p3=new Producer(queue);  
        //建立3个消费者  
        Consumer c1=new Consumer(queue);  
        Consumer c2=new Consumer(queue);  
        Consumer c3=new Consumer(queue);  
          
        //创建线程池  
        ExecutorService threadPool=Executors.newCachedThreadPool();  
        threadPool.execute(p1);//启动生产者线程  
        threadPool.execute(p2);  
        threadPool.execute(p3);  
          
        threadPool.execute(c1);//启动消费者线程  
        threadPool.execute(c2);  
        threadPool.execute(c3);  
          
        Thread.sleep(10*1000);  
        //停止生产  
        p1.stop();  
        p2.stop();  
        p3.stop();//当消费者处理完缓冲区中所有数据,程序执行完毕  
          
        Thread.sleep(3000);  
        threadPool.shutdown();    
    }  
}  
生产者-消费者模式能够很好的对生产者线程和消费者线程进行解耦,优化了系统结构。同时由于 共享缓冲区的作用,允许两类线程存在执行速度上的差异,一定程度上缓解了性能瓶颈对系统运行的影响。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值