BlockingQueue是JDK中的常用接口类型,对于队列来讲,最常用的操作当属元素的入队与出队,而BlockQueue虽然叫阻塞队列,但是却通过对于入队和出队操作的分场景设计,实现了对多种场景需求的满足。需要阻塞的,需要抛出异常的,还是需要等待一个时间间隔不满足再失败的,都完美支持。
场景需求 | 抛出异常的 | 返回特定值的 | 阻塞的 | 超时的 |
插入元素(入队) | boolean add(e) | boolean offer(e) | void put(e) | boolean offer(e, time, unit) |
移除元素(出队) | boolean remove(Object o) | E poll() | E take() | E poll(time, unit) |
检查元素(只看看) | E element() | E peek() | 不适用 | 不适用 |
我们来看一下各个方法的出处。 1、add与remove方法其实是继承自Collection接口。 2、E element()与E peek(),offer(e),E poll()四个方法,是继承自Queue接口。 3、void put(e)与E take()这两个纯粹的阻塞方法,是BlockingQueue接口特有的。 4、boolean offer(e, time, unit)与E poll(time, unit)这两个超时,其实也与阻塞有关,其实是可超时的阻塞。 从这几个角度理解这8+2个接口,就会非常清晰了,6个接口是爹生出它来的时候天生就带着的,其他4个接口才是真正的与阻塞队列特性相关的。这样,我们就能够在不同的场景需求下,精确的使用特定方法来进行处理。 举个例子,阻塞队列通常用于异步解耦,当接收并处理消息的线程在队列上轮询时,如果使用E poll()会出现空循环,那我们应该用E take()阻塞方法最佳,有消息就返回,没消息就呆着,但遇到需要批量处理时就会有问题。假定原计划10笔一批,结果第10笔阻塞住,怎么等也等不来了,可怎么好? 先检查下?然后再取?是一种办法。 这时候可以考虑poll(time, unit),等上几毫秒,如果等不来,且有已经组到半截的包(比如7笔)则结束当前包,形成一个批次;如果是空的,则进入下一此循环。这样既能够避免空转,也可以解决实时性问题。 BlockingQueue提供的这几个方法,组合使用,能够解决日常场景中的大部分需求,准确恰当的理解每个方法的使用场景,以及背后的原因和设计考虑也就变得非常有必要。