生产者消费者模型(自己上锁与阻塞队列两版本)

一、什么是生产者消费者模型

    生产者与消费者模式就是一个多线程并发协作的模式,在这个模式中呢,一部分线程被用于去生产数据,另一部分线程去处理数据,于是便有了形象的生产者与消费者了。而为了更好的优化生产者与消费者的关系,便设立一个缓冲区,也就相当于一个数据仓库,当生产者生产数据时锁住仓库,不让消费者访问,当消费者消费时锁住仓库,不让生产者访问仓库。举一个简单的例子,有一个生产者生产包子,他将生产好的包子放到筐中,放完包子由消费者从筐中拿出包子使用。当然筐还有一个作用就是当筐中没有包子时便锁住筐,不让消费者去筐中再拿取东西,当筐中有包子时,不让生产者再向筐中放入包子。

二.代码实现

1.基于await()/signal()与可重入锁的代码

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by WanYue
 */

public class ProducerCustomerModel {
    //维护一个临界区域
    private Queue<Integer> queue = new LinkedList<Integer>();
    //区域的最大值
    final static int MAX = 10;

    ReentrantLock lock = new ReentrantLock();
    //表示队列满了
    Condition full = lock.newCondition();
    //表示队列是空的
    Condition empty = lock.newCondition();

    int readData() throws InterruptedException {
       Thread.sleep((long)Math.random()*1000);
       return (int) Math.floor(Math.random());
   }

   //Producer
    public void readDb() throws InterruptedException{
        //必须锁住不是一下子就超过10了 就死锁了。

         lock.lock();
//        synchronized (queue){
            //满了就不生产了
            if(queue.size() == MAX){
                //monitor休眠方法
               //  queue.wait();
                //condition的指定休眠
                full.await();
                return;
            }
            //如果存在一个就唤醒消费者来消费
            if(queue.size() == 1) empty.signalAll();
            //没有满 就去生产
            int data = readData();
            queue.add(data);
            lock.unlock();
//        }
    }


    //Consumer,它的作用是计算
    public void calculate() throws InterruptedException{

        lock.lock();
//        synchronized (queue){
            //如果队列没有东西,就无法消费
            if(queue.size() == 0){
//                queue.wait();
                //condition的指定休眠
                empty.await();
                return;
            }
            //只要有一个空位就让生产者来生产
            if(queue.size() == MAX - 1) full.signalAll();

            Integer data = queue.remove();
            System.out.println("queue-size:" + queue.size());
            data *= 100;
//        }
      lock.unlock();
    }

    /**
     * 直接用唤醒notify会被卡死
     * P0 P1
     * C0
     * P0 C0 (都处于sleeping) C0 -> notify(p0 丨 p1)
     *P1 执行后queue.size() == 1 ,唤醒了P0 ,P1,把生产的放入queue,size=2了, P0就开始生产,不会执行了notify,因为不可能等于1了,因此
     * 需要notifyAll()都要将其唤醒 强行一起唤醒,
     * 需要改进的, 用一个两个Condition解决full,empty
     */
    public static void main(String[] args) {
        ProducerCustomerModel p = new ProducerCustomerModel();

        //开100个线程开始生产,生产一般都比消费慢
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                while(true){
                    try {
                        p.readDb();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

        //一个消费者就可以了
       new Thread(() -> {
           //消费者一直来回去看是否有可以消费的没。  没有while只会执行一次
           while(true){
               try {
                   p.calculate();
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       }).start();
    }

}

2.基于阻塞队列的take()与put()的实现

实际上阻塞队列的底层还是ReentLock

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by WanYue
 */

public class BlockingQueueTest {

    public static void main(String[] args) {

        //记录取的次数
        AtomicInteger count = new AtomicInteger();

        BlockingQueue<Integer> queue;
        //queue = new LinkedBlockingQueue<>(10); //前10个有效,后面全是Null
     queue = new ArrayBlockingQueue<>(10);
//         queue = new LinkedBlockingDeque<>();
//        queue = new PriorityBlockingQueue<>();
//       queue = new SynchronousQueue<>();
//        queue = new DelayQueue<Integer>();

        //Producer,开30个线程去生产,说明只是生产30个
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                try {
                    //如果想一直生产那就这里用一个死循环即可
                        queue.put((int) (Math.random() * 1_000));

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }

        //Consumer,10个线程消费
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                //一直轮询去访问
                while(true){
                    //开始消费
                    Integer x = null;
                    try {
                        x = (Integer) queue.take();
                        count.getAndIncrement();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Receive: " + x  +" count:" + count);
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
            }).start();
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万小猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值