测试类不做任何修改
package com.bjsxt.commu2; |
商品工厂
import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; /** * 商品工厂 */ public class ProductFactory3 { // private List<String> list = new ArrayList<String>(); // private Lock lock = new ReentrantLock(); // private Condition produceCond = lock.newCondition(); // private Condition consumeCond = lock.newCondition(); private BlockingQueue blockingQueue; private int max = 10; public ProductFactory3() { // blockingQueue = new ArrayBlockingQueue(max);//固定长度的,一但指定不可改变 blockingQueue = new LinkedBlockingDeque(max);//不指定上限就是无限长的 } public ProductFactory3(int max) { this.max = max; // blockingQueue = new ArrayBlockingQueue(max);//固定长度 blockingQueue = new LinkedBlockingDeque(max); } /** * 生产商品 * @param productName */ public void produce(String productName){ // lock.lock(); // try { // //仓库满,就不在生产 // while (list.size()==max){ // System.out.println(Thread.currentThread().getName()+"仓库已满,目前商品数量"+""); // produceCond.await(); // } // //生产商品 // list.add(productName); //仓库满,提示仓库满了 if (blockingQueue.size()==max){ System.out.println(Thread.currentThread().getName()+"仓库已满,目前商品数量"+blockingQueue.size()); } // blockingQueue.add(productName);//仓库满了就报错并中断该线程 try { blockingQueue.put(productName);//仓库满了不报错,让该线程处于等待状态,并唤醒其他线程 System.out.println(Thread.currentThread().getName()+"生产商品:" + productName+",目前商品数量"+blockingQueue.size()); } catch (InterruptedException e) { e.printStackTrace(); } //唤醒消费者,通知消费者消费 /*没有指定唤醒哪个线程,消费者和生产者处在同一个等待队列, 可能唤醒一个生产者,我们让生产者再判断,生产者发现仓库满的,继续沉睡, 再唤醒别的生产者,我们让别的生产者再判断,别的生产者再判断发现仓库满的, 别的生产者继续沉睡,直到唤醒一个消费者。 如何让他再回去判断?把判断做成一个循环,不断反复判断, 不符合就跳出循环,符合就执行循环继续沉睡。 */ // consumeCond.signal(); // } catch (InterruptedException e) { // e.printStackTrace(); // }finally { // lock.unlock(); // // } } /** * 消费商品 */ public void consume() { // lock.lock(); // try { // while (list.size() == 0) { // System.out.println(Thread.currentThread().getName() + "仓库已经没有库存了,目前商品的数量:" +""); // consumeCond.await(); // } // String productName = list.remove(0); if (blockingQueue.size() == 0) { System.out.println(Thread.currentThread().getName() + "仓库已经没有库存了,目前商品的数量:" +blockingQueue.size()); } // String productName = (String) blockingQueue.remove();//仓库没有库存了就报错,并中断线程 try { String productName = (String) blockingQueue.take();//仓库没有库存了不报错,让该线程处于等待状态,并唤醒其他线程 System.out.println(Thread.currentThread().getName() + "消费商品:" + productName + ",目前商品的数量:" +blockingQueue.size()); } catch (InterruptedException e) { e.printStackTrace(); } // //唤醒生产者,通知生产者生产 // produceCond.signal(); // //唤醒所有的生产者,但是只有一个生产者首先获取Lock,然后进入就绪队列,通过调度获得CPU // produceCond.signalAll(); // } catch (InterruptedException e) { // e.printStackTrace(); // }finally { // lock.unlock(); // // } } } |
BlockingQueue 阻塞队列
一、什么是BlockingQueue
BlockingQueue即阻塞队列,从阻塞这个词可以看出,在某些情况下对阻塞队列的访问可能会造成阻塞。被阻塞的情况主要有如下两种:
1. 当队列满了的时候进行入队列操作
2. 当队列空了的时候进行出队列操作
因此,当一个线程试图对一个已经满了的队列进行入队列操作时,它将会被阻塞,除非有另一个线程做了出队列操作;同样,当一个线程试图对一个空队列进行出队列操作时,它将会被阻塞,除非有另一个线程进行了入队列操作。
在Java中,BlockingQueue的接口位于 java.util.concurrent 包中(在Java5版本开始提供),由上面介绍的阻塞队列的特性可知,阻塞队列是线程安全的。
新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利
二、BlockingQueue的方法
| 抛出异常 | 特殊值 | 阻塞 | 超时 |
插入 | add(e) | offer(e) | put(e) | offer(e, time, unit) |
移除 | remove() | poll() | take() | poll(time, unit) |
检查 | element() | peek() | 不可用 | 不可用 |
四组不同的行为方式解释:
1(异常)
如果试图的操作无法立即执行,抛一个异常。
2(特定值)
如果试图的操作无法立即执行,返回一个特定的值(常常是 true / false)。
3(阻塞)
如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。
4(超时)
如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等待时间不会超过给定值。返回一个特定值以告知该操作是否成功(典型的是 true / false)。
BlockingQueue :不接受 null 元素。试图 add、put 或 offer 一个 null 元素时,某些实现会抛出 NullPointerException。null 被用作指示 poll 操作失败的警戒值。
BlockingQueue: 可以是限定容量的。它在任意给定时间都可以有一个 remainingCapacity,超出此容量,便无法无阻塞地 put 附加元素。没有任何内部容量约束的 BlockingQueue 总是报告Integer.MAX_VALUE 的剩余容量。
BlockingQueue :实现是线程安全的。所有排队方法都可以使用内部锁或其他形式的并发控制来自动达到它们的目的。
三、常见BlockingQueue
4.1 ArrayBlockingQueue
ArrayBlockingQueue是一个有边界的阻塞队列,它的内部实现是一个数组。有边界的意思是它的容量是有限的,我们必须在其初始化的时候指定它的容量大小,容量大小一旦指定就不可改变。
ArrayBlockingQueue是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。
此类支持对等待的生产者线程和使用者线程进行排序的可选公平策略。默认情况下,不保证是这种排序。然而,通过将公平性 (fairness) 设置为 true 而构造的队列允许按照 FIFO 顺序访问线程。公平性通常会降低吞吐量,但也减少了可变性和避免了“不平衡性”
4.3 LinkedBlockingQueue
LinkedBlockingQueue阻塞队列大小的配置是可选的,如果我们初始化时指定一个大小,它就是有边界的,如果不指定,它就是无边界的。说是无边界,其实是采用了默认大小为 Integer.MAX_VALUE
的容量 。它的内部实现是一个链表。
和ArrayBlockingQueue一样,LinkedBlockingQueue 也是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。
因篇幅问题不能全部显示,请点此查看更多更全内容