阻塞队列是Java5的内容,定义了阻塞队列的接口java.util.concurrent.BlockingQueue,
阻塞队列的概念是,一个指定长度的队列,如果队列满了,添加新元素的操作会被阻塞等待,
直到有空位为止。同样,当队列为空时候,请求队列元素的操作同样会阻塞等待,直到有可用元素为止
java.util.concurrent.BlockingQueue是一个队列,在进行检索或移除一个元素的时候,
它会等待队列变为非空;当在添加一个元素时,它会等待队列中的可用空间。BlockingQueue
接口是Java集合框架的一部分,主要用于实现生产者-消费者模式。不需要担心等待生产者
有可用的空间,或消费者有可用的对象,因为它都在BlockingQueue的实现类中被处理了。
Java提供了集中BlockingQueue的实现,比如ArrayBlockingQueue、LinkedBlockingQueue、
PriorityBlockingQueue,、SynchronousQueue等。
| 抛出异常(操作无法立即执行则抛出异常) | 特殊值(操作无法立即执行则返回一个特定值true / false) | 阻塞 | 超时 | |
| 插入 | add(e)在添加元素的时候,若超出了阻塞队列的长度会直接抛出异常 | offer(e)如果发现队列已满无法添加的话,会直接返回false | put(e)添加元素时发现队列已经满了会发生阻塞一直等待空间,以加入元素 | offer(e,time,unit) |
| 移除 | remove()若队列为空抛出NoSuchElementException异常 | poll()若队列为空,返回null | take()若队列为空,发生阻塞,等待有元素 | poll(time,unit) |
| 检查 | element() | peek() |
阻塞队列与平常接触的普通队列LinkedList或ArrayList等的最大不同点,在于阻塞队列支出阻塞添加和阻塞删除方法。
- 阻塞添加:所谓的阻塞添加是指当阻塞队列元素已满时,队列会阻塞加入元素的线程,直队列元素不满时才重新唤醒线程执行元素加入操作
- 阻塞删除:阻塞删除是指在队列元素为空时,删除队列元素的线程将被阻塞,直到队列不为空再执行删除操作(一般都会返回被删除的元素)
BlockingQueue 通常用于一个线程生产对象,而另外一个线程消费这些对象的场景。
JAVA并发包提供三个常用的并发队列实现,分别是:ConcurrentLinkedQueue、LinkedBlockingQueue和ArrayBlockingQueue。
使用阻塞队列实现的生产者消费者模式
阻塞的实现是依赖于阻塞队列实现的,put/take
- public class Producer extends Thread{
- private BlockingQueue
queue; - public Producer(BlockingQueue
queue) { - this.queue=queue;
- }
- public void run(){
- Random r=new Random();
- while(true){
- try{
- String temp="str_"+r.nextInt();
- queue.put(temp); //put方法会阻塞
- System.out.println("生产"+temp);
- Thread.sleep(20);
- } catch(InterruptedException e){
- e.printStackTrace();
- }
- }
- }
- }
-
-
- public class Consumer extends Thread{
- private BlockingQueue
queue; - public Producer(BlockingQueue
queue) { - this.queue=queue;
- }
- public void run(){
- while(true){
- try{
- String temp=queue.take(); //take会阻塞
- System.out.println("消费"+temp);
- Thread.sleep(10);
- } catch(InterruptedException e){
- e.printStackTrace();
- }
- }
- }
- }
测试类
- BlockingQueue
queue=new ArrayBlockingQueue<>(3); - new Producer(queue).start();
- new Thread(new Consumer(queue)).start();
BlockingQueue接口定义了一种阻塞的FIFO queue,每一个BlockingQueue都有一个容量,
让容量满时往BlockingQueue中添加数据时会造成阻塞,当容量为空时取元素操作会阻塞
- ArrayBlockingQueue是初始容器固定的阻塞队列,可以用来作为数据库模块成功竞拍的队
列,比如有10个商品,那么就设定一个10大小的数组队列。ArrayBlockingQueue的内部是通
过一个可重入锁ReentrantLock和两个Condition条件对象来实现阻塞
- LinkedBlockingQueue是一个阻塞的线程安全的队列,底层采用链表实现,入队和出队都
用了加锁,当队空的时候线程会暂时阻塞;它如果不指定容量,默认为Integer.MAX_VALUE,也就是无界队列。所以为了避免队列过大造成机器负载或者内存爆满的情况出现,我们在使用的时候建议手动传一个队列的大小。
- put方法:1、队列已满,阻塞等待。2、队列未满,创建一个node节点放入队列中,如果放
完以后队列还有剩余空间,继续唤醒下一个添加线程进行添加。如果放之前队列中没有元素,放完以后要唤醒消费线程进行消费
- take方法:1、队列为空,阻塞等待。2、队列不为空,从队首获取并移除一个元素,如果消
费后还有元素在队列中,继续唤醒下一个消费线程进行元素移除。如果放之前队列是满元素的情况,移除完后要唤醒生产线程进行添加元素。
ConcurrentLinkedQueue是一个基于链表的无界非阻塞队列,并且是线程安全的,它采用的是先进先出的规则,当我们增加一个元素时,它会添加到队列的末尾,当我们取一个元素时,它会返回一个队列头部的元素。不使用锁而是使用的是CAS原语无锁队列实现,是一个异步队列,入队速度很快,出队进行了加锁,性能稍慢;当多个线程共享访问一个公共collection 时,ConcurrentLinkedQueue 是一个恰当的选择。此队列不允许使用 null 元素