BlockingQueue
接口表示它是一个 Queue
,意思是存储在Queue 中的元素以先入先出(FIFO)顺序存储。在特定顺序插入的元素以相同的顺序检索 — 但是需要附加保证,从空队列检索一个元素的操作都会阻塞调用线程,直到这个项准备好被检索。同理,想要将一个元素插入到满队列的尝试也会导致阻塞调用线程,直到队列的存储空间可用。
BlockingQueue
干净利落地解决了如何将一个线程收集的项“传递”给另一线程用于处理的问题,无需考虑同步问题。
BlockingQueue 支持四种类型的操作。如下:
Throws exception | Special value | Blocks | Times out | |
Insert | add(e) | offer(e) | put(e) | offer(e, time, unit) |
Remove | remove() | poll() | take() | poll(time, unit) |
Examine | element() | peek() | not applicable | not applicable |
1 add(e) remove() element() 方法不会阻塞线程,当不满足约束条件时,会抛出IllegalStateException 异常。例如:当queue 被元素填满后,在调用add(e),则 该方法会抛出异常。
2 offer(e) poll() peek() 方法也不会阻塞线程,也会抛出异常。例如:当queue 被元素填满后,在调用offer(e),则不会插入元素,函数返回false。
3 想要实现阻塞功能,需要调用put(e) take() 方法。当不满足约束条件时,会阻塞线程。
下图为类的继承情况:其中ArrayBlockingQueue 和 LinkedBlockingQueue 是两个常用的类。Deque 为双向列表,即头和尾都能插入和删除元素。这两个类都是线程安全的。
我们在这里以ArrayBlockingQueue 为例,来看看其中的put 和take 方法的实现。
put 方法:
可以看出,ArrayBlockingQueue 内部由一个数组来实现队列的,内部数组的大小在构造函数中指定。插入前使用lock.lockInterruptibly() 进行了加锁。如果数组满了,就会等待(notFull.await() 实现);未满则插入。
take 方法:
实现方法和 put 大同小异。
用法实例: