在看netty的代码实现发现了TransferQueue,发现在jdk7已经加入。
在jdk7里的实现是LinkedTransferQueue。它实现了BlockingQueue的接口,并且提供了类似SynchronizeQueue的功能。由于采用了CAS的方式对线程进行同步,减少了锁的开销,性能相对与其他的队列实现有了很大的提升。其内部的实现是一个FiFo的Dual Quque。很多开源的项目,在jdk7之前,就早早的用上了这个东东,比如netty,bonecp,xmemcached…
TransferQueue的基本使用和BlockingQueue差不多,有点特殊的就是它的transfer接口。
TransferQueue是一个继承了 BlockingQueue的接口,并且增加若干新的方法。LinkedTransferQueue是实现类,其定义为一个无界的队列,一样具有先进先出(FIFO : first-in-first-out)的特性。
Doug Lea 这样评价它:
- transfer(E e)
若当前存在一个正在等待获取的消费者线程,即立刻移交之;否则,会插入当前元素e到队列尾部,并且等待进入阻塞状态,到有消费者线程取走该元素。 - tryTransfer(E e)
若当前存在一个正在等待获取的消费者线程(使用take()或者poll()函数),使用该方法会即刻转移/传输对象元素e;
若不存在,则返回false,并且不进入队列。
这是一个不阻塞的操作。 - tryTransfer(E e, long timeout, TimeUnit unit)
若当前存在一个正在等待获取的消费者线程,会立即传输给它;
否则将插入元素e到队列尾部,并且等待被消费者线程获取消费掉,
若在指定的时间内元素e无法被消费者线程获取,则返回false,同时该元素被移除。 - hasWaitingConsumer()
很明显,判断是否终端消费者线程 - getWaitingConsumerCount()
字面意思很明白,获取终端所有等待获取元素的消费线程数量 - size()
因为队列的异步特性,检测当前队列的元素个数需要逐一迭代,可能会得到一个不太准确的结果,尤其是在遍历时有可能队列发生更改。 - 批量操作
类似于 addAll,removeAll, retainAll, containsAll, equals, toArray 等方法,API不能保证一定会立刻执行。
因此,我们在使用过程中,不能有所期待,这是一个具有异步特性的队列。
或许,下次我们在构造一个线程池时,可以考虑使用TransferQueue:
public static ExecutorService newTransferCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new LinkedTransferQueue());
}
- 无论是transfer还是tryTransfer方法,在>=1个消费者线程等待获取元素时(此时队列为空),都会立刻转交,这属于线程之间的元素交换。注意,这时,元素并没有进入队列。
- 在队列中已有数据情况下,transfer将需要等待前面数据被消费掉,直到传递的元素e被消费线程取走为止。
- 使用transfer方法,工作者线程可能会被阻塞到生产的元素被消费掉为止
- 消费者线程等待为零的情况下,各自的处理元素入队与否情况有所不同。
- size()方法,需要迭代,可能不太准确,尽量不要调用。
或许,下次我们在构造一个线程池时,可以考虑使用TransferQueue:
参考:
jdk1.7 API http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/TransferQueue.html
http://www.blogjava.net/yongboy/archive/2012/02/04/369575.html