LinkedTransferQueue

LinkedTransferQueue 出身 Google ,现在已经被集成在 JDK7 中,但目前主流的 JDK 平台仍然是 JDK6 ,所以很多时候我们需要这样引入:

import com.google.code.yanf4j.util ;

她的作者依然是 Doug Lea 大神,他说:

LinkedTransferQueue 是一个聪明的队列,他是 ConcurrentLinkedQueue,

SynchronousQueue

(in “fair” mode

公平模式   ), and unbounded LinkedBlockingQueue   的超集。

我们从她的声明中也可以看出这一点:

public class

LinkedTransferQueue<E> extends AbstractQueue<E> implements

BlockingQueue<E>

Google 的注释中,有这样一段描述:

This class extends the approach used in FIFO-mode

SynchronousQueues. See the internal documentation, as well as

the PPoPP 2006 paper “Scalable Synchronous Queues” by Scherer,

Lea & Scott

http://www.cs.rice.edu/~wns1/papers/2006-PPoPP-SQ.pdf )

The main extension is to provide different Wait modes for the

main “xfer”

method that puts or takes items.  These

don’t

impact the basic dual-queue logic, but instead control whether

or how threads block upon insertion of request or data nodes

into the dual queue. It also uses slightly different

conventions for tracking whether nodes are off-list or

cancelled.

LinkedTransferQueue 实现了一个重要的接口 TransferQueue, 该接口含有下面几个重要方法:

1. transfer(E e)

若当前存在一个正在等待获取的消费者线程,即立刻移交之;否则,会插入当前元素 e 到队列尾部,并且等待进入阻塞状态,到有消费者线程取走该元素。

2. tryTransfer(E e)

若当前存在一个正在等待获取的消费者线程(使用 take() 或者 poll() 函数),使用该方法会即刻转移 / 传输对象元素 e ;若不存在,则返回 false ,并且不进入队列。这是一个不阻塞的操作。

3. tryTransfer(E e, long timeout, TimeUnit unit)

若当前存在一个正在等待获取的消费者线程,会立即传输给它 ; 否则将插入元素 e 到队列尾部,并且等待被消费者线程获取消费掉 , 若在指定的时间内元素 e 无法被消费者线程获取,则返回 false ,同时该元素被移除。

4. hasWaitingConsumer()

判断是否存在消费者线程

5. getWaitingConsumerCount()

获取所有等待获取元素的消费线程数量

6. size()

因为队列的异步特性,检测当前队列的元素个数需要逐一迭代,可能会得到一个不太准确的结果,尤其是在遍历时有可能队列发生更改。

7. 批量操作

类似于

addAll,removeAll,

retainAll, containsAll, equals, toArray 等方法,API不能保证一定会立刻执行。

因此,我们在使用过程中,对这些不能有所期待,这是一个具有异步特性的队列。

其实 transfer 方法在 SynchronousQueue 的实现中就已存在了 , 只是没有做为 API 暴露出来。SynchronousQueue 有一个特性 : 它本身不存在容量 , 只能进行线程之间的元素传送。 SynchronousQueue 在执行 offer 操作时,如果没有其他线程执行 poll ,则直接返回 false. 线程之间元素传送正是通过 transfer 方法完成的。

有一个使用案例,我们知道 ThreadPoolExecutor 调节线程的原则是:先调整到最小线程,最小线程用完后,他会将优先将任务放入缓存队列 (offer(task)), 等缓冲队列用完了,才会向最大线程数调节。这似乎与我们所理解的线程池模型有点不同。我们一般采用增加到最大线程后,才会放入缓冲队列中,以达到最大性能。 ThreadPoolExecutor 代码段:

如果我们采用 SynchronousQueue 作为 ThreadPoolExecuto 的缓冲队列时,在没有线程执行 poll 时 ( 即存在等待线程 ) ,则 workQueue.offer(command) 返回 false, 这时 ThreadPoolExecutor就会增加线程,最快地达到最大线程数。但也仅此而已,也因为 SynchronousQueue 本身不存在容量 , 也决定了我们一般无法采用 SynchronousQueue 作为 ThreadPoolExecutor 的缓存队列。而一般采用 LinkedBlockingQueue 的 offer 方法来实现。最新的 LinkedTransferQueue 也许可以帮我们解决这个问题,后面再说。

transfer 算法比较复杂,实现很难看明白。大致的理解是采用所谓双重数据结构 (dual data structures) 。之所以叫双重,其原因是方法都是通过两个步骤完成:

保留与完成。比如消费者线程从一个队列中取元素,发现队列为空,他就生成一个空元素放入队列 , 所谓空元素就是数据项字段为空。然后消费者线程在这个字段上旅转等待。这叫保留。直到一个生产者线程意欲向队例中放入一个元素,这里他发现最前面的元素的数据项字段为 NULL,他就直接把自已数据填充到这个元素中,即完成了元素的传送。大体是这个意思,这种方式优美了完成了线程之间的高效协作。

对于

LinkedTransferQueue,Doug

Lea

进行了尽乎极致的优化。   Grizzly   的采用了   PaddedAtomicReference  

PaddedAtomicReference 相对于父类 AtomicReference 只做了一件事情,就将共享变量追加到64 字节。我们可以来计算下,一个对象的引用占 4 个字节,

它追加了 15 个变量共占 60 个字节,再加上父类的 Value 变量,一共 64 个字节。 上面追加15个4字节对象的代码看起来是不是很奇葩?其实不是的 , 这样做是为了在并发中提升效率。

为什么追加 64 字节能够提高并发编程的效率呢 ? 因为对于英特尔酷睿 i7 ,酷睿, Atom 和 NetBurst , Core Solo 和

Pentium

M

处理器的   L1     L2     L3   缓存的高速缓存行是   64   个字节宽,不支持部分填充缓存行,这意味着如果队列的头节点和尾节点都不足   64   字节的话,处理器会将它们都读到同一个高速缓存行中,在多处理器下每个处理器都会缓存同样的头尾节点,当一个处理器试图修改头接点时会将整个缓存行锁定,那么在缓存一致性机制的作用下,会导致其他处理器不能访问自己高速缓存中的尾节点,而队列的入队和出队操作是需要不停修改头接点和尾节点,所以在多处理器的情况下将会严重影响到队列的入队和出队效率。   Doug lea   使用追加到   64   字节的方式来填满高速缓冲区的缓存行,避免头接点和尾节点加载到同一个缓存行,使得头尾节点在修改时不会互相锁定。

那么是不是在使用 Volatile 变量时都应该追加到 64 字节呢?不是的。在两种场景下不应该使用这种方式。第一: 缓存行非 64 字节宽的处理器 ,如 P6 系列和奔腾处理器,它们的 L1 和 L2 高速缓存行是 32 个字节宽。第二: 共享变量不会被频繁的写 。因为使用追加字节的方式需要处理器读取更多的字节到高速缓冲区,这本身就会带来一定的性能消耗,共享变量如果不被频繁写的话,锁的几率也非常小,就没必要通过追加字节的方式来避免相互锁定。

Java 线程池是一种用于管理线程执行的工具,它可以帮助开发人员有效地管理并发任务的执行。而 LinkedTransferQueue 是 Java 并发包中的一个数据结构,它可以在多个线程之间进行数据传输。 使用线程池,可以避免频繁地创建和销毁线程,从而提高系统的性能,减少资源的消耗。线程池可以管理线程的生命周期,包括线程的创建、执行和销毁。线程池维护了一个线程队列,任务被提交到线程池中后,线程池会从队列中获取一个空闲的线程来执行任务。当任务执行完毕后,线程可以被重复利用,不需要销毁。线程池还可以设置线程的数量、线程的优先级以及队列的大小等参数,以适应不同的并发需求。 LinkedTransferQueue 是一种基于链表的无界队列,它提供了一种更高级的功能,即数据传输。该队列可以在多个线程之间传递数据,不仅仅是简单的数据存储和获取。它提供了 put、take 和 transfer 等方法,可以安全地在多个线程之间传递元素。而且,LinkedTransferQueue 还具备一些阻塞队列的特性,当队列为空时,take 方法会被阻塞,直到队列中有元素;当队列已满时,put 方法也会被阻塞,直到有空间插入元素。 在线程池中使用 LinkedTransferQueue,可以实现更灵活的任务调度和数据传输。例如,可以将生产者线程产生的数据放入 LinkedTransferQueue,然后由消费者线程从队列中获取并处理数据。这种方式可以实现线程之间的解耦,提高系统的并发性能和吞吐量。 总之,Java 线程池和 LinkedTransferQueue 是两个在高并发场景中非常有用的工具。线程池可以提高系统的性能和资源利用率,而 LinkedTransferQueue 则提供了数据传输的功能,使线程间的协作更加灵活和高效。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值