49、Java 生产者/消费者模式亲手实测采坑

博客介绍了生产者消费者模型,即系统中有生产者、消费者和内存缓冲区,生产者存数据,消费者取数据,仓库有存取规则。还给出三种Java实现模型,分别用wait()、notifyAll(),ReentrantLock和Condition,以及BlockingDeque的方法,均用ArrayList作缓冲区。

生产者消费者模型具体来讲,就是在一个系统中,存在生产者和消费者两种角色和一个内存缓冲区。
一个或者多个生产者 产生商品(数据) 并将商品存放到仓库中(内存缓冲区);
一个或者多个消费者 消费商品(数据)并将商品从仓库中取出(内存缓冲区);
仓库有存取功能,并且当仓库存满数据时,只取不存;当仓库没有商品时,只存不取。

  • 生产者持续生产,直到缓冲区满,阻塞;缓冲区不满后,继续生产
  • 消费者持续消费,直到缓冲区空,阻塞;缓冲区不空后,继续消费
  • 生产者可以有多个,消费者也可以有多个
模型一

使用 wait() 、 notifyAll()来进行阻塞和唤醒线程,使用ArrayList 从当缓冲区。

定义一个缓存队列

/**
 * Created  By : fumm
 * DATE : 2019/7/11  16:13
 * Describe : 公共队列
 *  生产者/消费者设计模式顾名思义就是两个互斥线程,一个负责生产,一个负责消费,两者是线程不安全的;
 *  为了保证互斥线程的安全性,需要做对应的处理,以上代码使用了synchronized 、wait()、notifyAll()来保证。
 **/
public class PublicQueue<T> {
    private int MAX_NUM = 50;
    private List<T> mQ = new ArrayList<>();
    private LinkedHashMap<Integer, T> linkedHashMap = new LinkedHashMap<>();//缓冲区
    public synchronized  void add(T t) {
        // 不安全 (如果线程运行至此处刚好切换到其他线程 操作mQ ,mQ.size可能会变化)
        // int size = mQ.size();
        if (mQ.size()<= MAX_NUM) {
        	// 仓库未满 添加
            mQ.add(t);
            System.out.println("生产者生产 :" + t.toString());
            notifyAll();
        } else {
            try {
                System.out.println("生产者 :等待");
                // 仓库已满 当前线程等待
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public synchronized  void remove() {
        // int size = mQ.size();
        if (mQ.size() <= 0) {
            // 当前线程等待
            try {
                System.out.println("消费者 :等待");
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            T t = mQ.remove(size - 1);
            System.out.println("消费者消费 :" + t.toString());
            notifyAll();
        }
    }
}

定义一个生产者

/**
 * Created  By : fumm
 * DATE : 2019/7/11  16:23
 * Describe : 生产者线程
 **/
public class ProduceThread extends Thread {
    private PublicQueue mQueue;

    public ProduceThread(PublicQueue mQueue) {
        this.mQueue = mQueue;
    }

    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 100000; i++) {
            mQueue.add("i =" + i);
        }
    }
}

定义一个消费者

/**
 * Created  By : fumm
 * DATE : 2019/7/11  16:23
 * Describe : 消费者线程
 **/
public class ConsumerThread extends Thread {
    private PublicQueue mQueue;

    public ConsumerThread(PublicQueue mQueue) {
        this.mQueue = mQueue;
    }

    @Override
    public void run() {
        super.run();
        while (true) {
            mQueue.remove();
        }
    }
}
模型二

使用 ReentrantLock.lock() Condition.await()[等待] Condition.signalAll() [唤醒]、,使用ArrayList 从当缓冲区。

/**
 * Created  By : fumm
 * DATE : 2019/7/11  17:04
 * Describe : 生产者 消费者 模型2
 *  定义一个缓存队列
 **/
public class PublicQueue2<T> {
    private List<T> mQ = new ArrayList<>();
    //    private LinkedHashMap<Integer, T> mQ = new LinkedHashMap<>();//缓冲区
    private int MAX_NUM = 50;
    private int putIndex = 0;//数据插入的角标
    private final Condition mAddCondition;
    private final Condition mRemoveCondition;
    private final ReentrantLock mLock;


    public PublicQueue2() {
        mLock = new ReentrantLock();
        mAddCondition = mLock.newCondition();
        mRemoveCondition = mLock.newCondition();

    }

    public void add(T t) {

        try {
            mLock.lock();
            if (mQ.size() == MAX_NUM) {
                System.out.println("生产者 :等待" + "----size:" + mQ.size());
                // 生产者阻塞等待
                mAddCondition.await();
            }
            mQ.add(t);
//            mQ.put(putIndex, t);
            System.out.println("生产者生产 :" + t.toString());
            putIndex = (putIndex + 1 >= MAX_NUM) ? (putIndex + 1) % MAX_NUM : putIndex + 1;
			// 唤醒消费者 
            mRemoveCondition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            mLock.unlock();
        }
    }


    public void remove() {
        T t = null;
        try {
            mLock.lock();
            if (mQ.size() == 0) {
                System.out.println("消费者 :等待" + "----size:" + mQ.size());
                // 消费者等待 阻塞
                mRemoveCondition.await();
            }
//            Iterator it = mQ.entrySet().iterator();
//            if (it.hasNext()) {
//                Map.Entry<Integer, T> entry = (Map.Entry<Integer, T>) it.next();
//                t = entry.getValue();
//                int index = entry.getKey();
//                mQ.remove(index);
//                System.out.println("消费者消费 :" + t.toString());
//            }
            mQ.remove(mQ.size() - 1);
            mAddCondition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            mLock.unlock();
        }
    }

	// 定义一个生产者
    public static class ProduceThread extends Thread {
        private PublicQueue2 mQ;
        public ProduceThread(PublicQueue2 mQ) {
            this.mQ = mQ;
        }

        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 1000; i++) {
                mQ.add("--i= " + i);
            }
        }
    }
	// 定义一个消费者
    public static class ConsumerThread extends Thread {
        private PublicQueue2 mQ;

        public ConsumerThread(PublicQueue2 mQ) {
            this.mQ = mQ;
        }

        @Override
        public void run() {
            super.run();
            while (true) {
                mQ.remove();
            }
        }
    }
}
模型三

使用 BlockingDeque.put(t) 和 BlockingDeque.take(),使用ArrayList 从当缓冲区。

/**
 * Created  By : fumm
 * DATE : 2019/7/12  9:07
 * Describe : TODO
 **/
public class PublicQueue4<T> {
    private static final int MAX_NUM = 5;
    private BlockingDeque<T> blockingDeque = new LinkedBlockingDeque<>(MAX_NUM);//缓冲区


    public void add(T t) {
//        blockingDeque.add(t);
        try {
            Thread.sleep((long) (Math.random() * 300));
            blockingDeque.put(t);
            System.out.println("生产一个产品:" + t.toString() + "---剩余空间:" + (MAX_NUM - blockingDeque.size()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void remove() {
//        blockingDeque.remove();
        try {
            System.out.println("仓库产品数量:" + blockingDeque.size());
            T t = blockingDeque.take();
            System.out.println("消费一个产品:" + t.toString() + "---剩余产品:" + blockingDeque.size());
            Thread.sleep(200 + (long) (Math.random() * 100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    public static class ProduceThread extends Thread {
        private PublicQueue4 mQueue4;

        public ProduceThread(PublicQueue4 queue4) {
            this.mQueue4 = queue4;
        }

        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 100; i++) {
                mQueue4.add("i--->" + i);
            }
        }
    }

    public static class ConsumerThread extends Thread {
        private PublicQueue4 mQueue4;

        public ConsumerThread(PublicQueue4 mQueue4) {
            this.mQueue4 = mQueue4;
        }

        @Override
        public void run() {
            super.run();
            while (true){
                mQueue4.remove();
            }
        }
    }

}

测试类

/**
 * Created  By : fumm
 * DATE : 2019/7/11  15:50
 * Describe : 测试生产者 和消费者模型
 * 1、生产者生产数据到缓冲区中,消费者从缓冲区中取数据。
 * 2、如果缓冲区已经满了,则生产者线程阻塞;
 * 3、如果缓冲区为空,那么消费者线程阻塞。
 **/
public class Test {


    public static void main(String[] args) {
        PublicQueue<String> queue = new PublicQueue<>();
        // 生产者 消费者 模型一
//        ProduceThread produceThread = new ProduceThread(queue);
//        ConsumerThread consumerThread = new ConsumerThread(queue);
//        produceThread.start();
//        consumerThread.start();

        // 生产者 消费者 模型二
//        PublicQueue2<String> queue2 = new PublicQueue2<>();
//        PublicQueue2.ProduceThread produceThread = new PublicQueue2.ProduceThread(queue2);
//        PublicQueue2.ConsumerThread consumerThread = new PublicQueue2.ConsumerThread(queue2);
//        produceThread.start();
//        consumerThread.start();


        // 生产者 消费者 模型二
        PublicQueue3<String> queue3 = new PublicQueue3<>();
        PublicQueue3.ProduceThread produceThread = new PublicQueue3.ProduceThread(queue3);
        PublicQueue3.ConsumerThread consumerThread = new PublicQueue3.ConsumerThread(queue3);
        produceThread.start();
        consumerThread.start();

        // 生产者 消费者 模型四
//        PublicQueue4<String> queue4 = new PublicQueue4<>();
//        PublicQueue4.ConsumerThread c1 = new PublicQueue4.ConsumerThread(queue4);
//        PublicQueue4.ProduceThread p1= new PublicQueue4.ProduceThread(queue4);
//        c1.start();
//        p1.start();
    }

}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值