JAVA多线程学习之生产者消费者模式(三)

生产者消费者模式是并发、多线程编程中经典的设计模式,生产者和消费者通过分离的执行工作解耦,简化了开发模式。生产者和消费者模式在生活当中随处可见,它描述的是协调与协作的关系。比如我去包子铺买包子,店铺老板属于生产者,而我属于消费者。而包子铺的蒸笼属于我与店铺老板的媒介。店铺老板生产包子,把它放到蒸笼上去蒸,包子熟了,我从蒸笼里去拿包子。如果蒸笼里包子放满了,店铺老板就停止做包子,等待包子销售出去;如果蒸笼的包子都卖完了,则我停止拿包子,等待店铺老板做包子。

对于这种情况,我们有三种方式来实现。

第一种,wait()和notify()实现线程通信。

/**
 * 
 * @author saule    
 * @date  2019年3月26日 下午6:30:54
 * @Description 包子容器,即蒸笼
 */
public class Bread {
    private int count=0;
    
    public synchronized void increase(){
        //如果蒸笼里包子等于6个,则等待,不生产
        while(count==6){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count++;
        System.out.println("生产包子成功!包子数量为"+count);
        this.notifyAll();
    }
    
    public synchronized void decrease(){
        //如果蒸笼包子没了,则等待,不再消费包子
        while(count==0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count--;
        System.out.println("消费包子成功!包子数量为"+count);
        this.notifyAll();
    }
}
/**
 * 
 * @author saule    
 * @date  2019年3月26日 下午7:12:27
 * @Description 生产者
 */
public class Producer implements Runnable {
    private Bread bread;
    
    public Producer(Bread bread) {
        this.bread = bread;
    }

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            bread.increase();
            
        }

    }

}
/**
 * 
 * @author saule    
 * @date  2019年3月26日 下午7:12:14
 * @Description 消费者
 */
public class Consumer implements Runnable {
    private Bread bread;
    
    public Consumer(Bread bread){
        this.bread=bread;
    }
    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            bread.decrease();
        }

    }

}
public class BreadDemo {
    public static void main(String[] args) {
        Bread bread=new Bread();
        Producer producer=new Producer(bread);
        Consumer consumer=new Consumer(bread);
        
        Thread t1=new Thread(producer);
        Thread t2=new Thread(consumer);
        
        t1.start();
        t2.start();
    }
}
测试结果:
生产包子成功!包子数量为1
生产包子成功!包子数量为2
生产包子成功!包子数量为3
生产包子成功!包子数量为4
生产包子成功!包子数量为5
生产包子成功!包子数量为6
消费包子成功!包子数量为5
生产包子成功!包子数量为6
消费包子成功!包子数量为5
生产包子成功!包子数量为6
消费包子成功!包子数量为5

 第二种,采用阻塞队列实现

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

/**
 * 
 * @author saule    
 * @date  2019年3月26日 下午7:44:56
 * @Description 包子容器
 */

public class Bread {
    private BlockingQueue queue=new LinkedBlockingDeque(10);
    
    //向蒸笼放包子
    public void increase(){
        try {
            queue.put(1);
            System.out.println("店铺老板做了个包子,目前蒸笼里有"+queue.size()+"个包子。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    //从蒸笼拿包子
    public void decrease(String name){
        try {
            queue.take();
            System.out.println(name+"吃了个包子,目前蒸笼里还有"+queue.size()+"个包子。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


/**
 * 
 * @author saule    
 * @date  2019年3月26日 下午7:55:31
 * @Description 生产者
 */
public class ProducerQueue implements Runnable {
    private Bread bread;
    
    public ProducerQueue(Bread bread) {
        this.bread = bread;
    }

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            bread.increase();
        }
    }

}


/**
 * 
 * @author saule    
 * @date  2019年3月26日 下午8:05:34
 * @Description 消费者
 */
public class ConsumerQueue implements Runnable {
    private Bread bread;
    
    public ConsumerQueue(Bread bread) {
        this.bread = bread;
    }
    
    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            bread.decrease(Thread.currentThread().getName());
        }
    }

}


public class BreadDemo {
    public static void main(String[] args) {
        Bread bread=new Bread();
        
        ProducerQueue pq=new ProducerQueue(bread);
        ConsumerQueue cq=new ConsumerQueue(bread);
        
        Thread p=new Thread(pq);
        
        Thread c1=new Thread(cq,"顾客一");
        Thread c2=new Thread(cq,"顾客二");
        Thread c3=new Thread(cq,"顾客三");
        
        p.start();
        c1.start();
        c2.start();
        c3.start();
        
    }
}

测试结果:

店铺老板做了个包子,目前蒸笼里有1个包子。
店铺老板做了个包子,目前蒸笼里有2个包子。
店铺老板做了个包子,目前蒸笼里有3个包子。
店铺老板做了个包子,目前蒸笼里有4个包子。
店铺老板做了个包子,目前蒸笼里有5个包子。
顾客一吃了个包子,目前蒸笼里还有4个包子。
顾客三吃了个包子,目前蒸笼里还有3个包子。
顾客二吃了个包子,目前蒸笼里还有2个包子。
店铺老板做了个包子,目前蒸笼里有3个包子。
店铺老板做了个包子,目前蒸笼里有4个包子。
店铺老板做了个包子,目前蒸笼里有5个包子。
店铺老板做了个包子,目前蒸笼里有6个包子。
店铺老板做了个包子,目前蒸笼里有7个包子。
店铺老板做了个包子,目前蒸笼里有8个包子。
顾客一吃了个包子,目前蒸笼里还有7个包子。

 第三种,采用lock和condition实现

/**
 * 
 * @author saule    
 * @date  2019年3月26日 下午11:53:53
 * @Description 公共资源
 */
public class Resource {
    //当前资源数量
    private int num=0;
    //最大资源数量
    private int size=10;
    private Lock lock;
    private Condition producerCondition;
    private Condition consumerCondition;
    public Resource(Lock lock, Condition producerCondition,Condition consumerCondition) {
        this.lock = lock;
        this.producerCondition = producerCondition;
        this.consumerCondition = consumerCondition;
    }
    
    /**
     * 向资源池添加资源
     */
    public void increase(){
        lock.lock();
        try{
            if(num<size){
                num++;
                System.out.println(Thread.currentThread().getName()+"生产了一件资源,当前资源池有"+num+"个");
                //唤醒消费者
                consumerCondition.signalAll();
            }else{
                //让生产线程等待
                try {
                    producerCondition.await();
                    System.out.println("waiting1...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
        }finally{
            lock.unlock();
        }
    }
    
    /**
     * 从资源池中取走资源
     */
    public void decrease(){
        lock.lock();
        try{
            if(num>0){
                num--;
                System.out.println(Thread.currentThread().getName()+"消费了一件资源,当前资源池有"+num+"个");
                //唤醒生产者
                producerCondition.signalAll();
            }else{
                //让消费线程等待
                try {
                    consumerCondition.await();
                    System.out.println("waiting2...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
        }finally{
            lock.unlock();
        }
        
    }
    
    
}


/**
 * 
 * @author saule    
 * @date  2019年3月27日 上午12:10:58
 * @Description 生产者
 */
public class ProducerThread implements Runnable {
    private Resource resource;

    public ProducerThread(Resource resource) {
        this.resource = resource;
    }
    
    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.increase();
        }
    }
    
}


/**
 * 
 * @author saule    
 * @date  2019年3月27日 上午12:21:52
 * @Description 消费者
 */
public class ConsumerThread implements Runnable {
    private Resource resource;

    public ConsumerThread(Resource resource) {
        this.resource = resource;
    }
    
    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.decrease();
        }
    }
}


public class LockCondition {
    public static void main(String[] args) {
        Lock lock=new ReentrantLock();
        Condition producerCondition=lock.newCondition();
        Condition consumerCondition=lock.newCondition();
        Resource resource=new Resource(lock, producerCondition, consumerCondition);
        
        ProducerThread pt=new ProducerThread(resource);
        ConsumerThread ct=new ConsumerThread(resource);
        
        Thread tp=new Thread(pt, "生产者");
        
        Thread ct1=new Thread(ct, "消费者1");
        Thread ct2=new Thread(ct, "消费者2");
        Thread ct3=new Thread(ct, "消费者3");
        
        tp.start();
        ct1.start();
        ct2.start();
        ct3.start();
    
    }
}

测试结果:
生产者生产了一件资源,当前资源池有1个
生产者生产了一件资源,当前资源池有2个
生产者生产了一件资源,当前资源池有3个
生产者生产了一件资源,当前资源池有4个
生产者生产了一件资源,当前资源池有5个
生产者生产了一件资源,当前资源池有6个
生产者生产了一件资源,当前资源池有7个
生产者生产了一件资源,当前资源池有8个
生产者生产了一件资源,当前资源池有9个
消费者1消费了一件资源,当前资源池有8个
消费者3消费了一件资源,当前资源池有7个
消费者2消费了一件资源,当前资源池有6个
生产者生产了一件资源,当前资源池有7个
生产者生产了一件资源,当前资源池有8个
生产者生产了一件资源,当前资源池有9个
生产者生产了一件资源,当前资源池有10个
消费者1消费了一件资源,当前资源池有9个
消费者3消费了一件资源,当前资源池有8个
消费者2消费了一件资源,当前资源池有7个

 

posted @ 2019-03-27 10:26  一棵写代码的柳树  阅读(309)  评论(0)    收藏  举报