关联较强的上一篇:
[java基础-集合篇]有界阻塞队列ArrayBlockingQueue源码解析-CSDN博客
总的来说。LinkedBlockingQueue 是一个基于链表节点的自定大小的线程安全的阻塞队列。遵循FIFO,结构上一端进一端出的单向队列。
源码注释
|
翻译
|
An optionally-bounded blocking queue based on linked nodes. This queue orders elements FIFO (first-in-first-out). The head of the queue is that element that has been on the queue the longest time. The tail of the queue is that element that has been on the queue the shortest time. New elements are inserted at the tail of the queue, and the queue retrieval operations obtain elements at the head of the queue. Linked queues typically have higher throughput than array-based queues but less predictable performance in most concurrent applications.
The optional capacity bound constructor argument serves as a way to prevent excessive queue expansion. The capacity, if unspecified, is equal to Integer.MAX_VALUE. Linked nodes are dynamically created upon each insertion unless this would bring the queue above capacity.
This class and its iterator implement all of the optional methods of the Collection and Iterator interfaces.
This class is a member of the Java Collections Framework.
|
基于链接节点的可选有界 阻塞队列 。此队列对元素 FIFO (先进先出) 进行排序。队列的 头部 是在队列中停留时间最长的元素。队列的 尾 部是在队列中时间最短的元素。新元素入到队列的尾部,队列检索操作在队列的头部获取元素。链接队列通常比基于阵列的队列具有更高的吞吐量,但在大多数并发应用程序中,性能的可预测性较差。
可选的 capacity bound constructor 参数用作防止过度队列扩展的一种方式。如果未指定,则 capacity 等于 Integer.MAX_VALUE。链接节点在每次插入时动态创建,除非这会使队列超过容量。
这个类及其迭代器实现了 and Iterator 接口的所有Collection可选方法。
此类是 Java Collections Framework 的成员
|
A variant of the "two lock queue" algorithm. The putLock gates
entry to put (and offer), and has an associated condition for
waiting puts. Similarly for the takeLock. The "count" field
that they both rely on is maintained as an atomic to avoid
needing to get both locks in most cases. Also, to minimize need
for puts to get takeLock and vice-versa, cascading notifies are
used. When a put notices that it has enabled at least one take,
it signals taker. That taker in turn signals others if more
items have been entered since the signal. And symmetrically for
takes signalling puts. Operations such as remove(Object) and
iterators acquire both locks.
Visibility between writers and readers is provided as follows:
Whenever an element is enqueued, the putLock is acquired and
count updated. A subsequent reader guarantees visibility to the
enqueued Node by either acquiring the putLock (via fullyLock)
or by acquiring the takeLock, and then reading n = count.get();
this gives visibility to the first n items.
To implement weakly consistent iterators, it appears we need to
keep all Nodes GC-reachable from a predecessor dequeued Node.
That would cause two problems:
|
这是“双锁队列”算法的一个变体。putLock控制进入put(和offer)方法,并有一个关联的条件用于等待put操作。
takeLock同样如此。
它们都依赖的“count”字段被维护为原子操作,以避免在大多数情况下需要获取两个锁。
此外,为了最小化put操作需要获取takeLock以及反之亦然的需求,这里使用了级联通知。
当一个put操作发现它至少使得一个take操作成为可能时,它会向取者发送信号。
如果自信号发出以来有更多元素加入,则该取者进而会向其他取者发送信号。
对put操作向take操作发送信号的情况也是类似的处理方式。
诸如remove(Object)和迭代器的操作需要同时获取这两个锁。
写入者和读者之间的可见性通过以下方式提供:
每当一个元素被入队时,就会获取putLock并更新count。
随后的读者通过要么获取putLock(通过fullyLock方法)要么获取takeLock然后读取n = count.get()来保证能看到入队节点的可见性;这使得前n个项是可见的。
为了实现弱一致性的迭代器,看起来我们需要保持所有节点从一个被移除的前置节点GC可达。这会导致两个问题:
这种设计确保了高并发性能,同时也考虑到了内存管理和垃圾回收的影响,旨在减少不必要的内存占用,并避免由于对象引用跨越不同的垃圾回收代而导致的复杂性。此外,它还讨论了如何通过最小化获取锁的需求和利用级联通知来优化性能。对于迭代器,文中提到了维持弱一致性所带来的挑战,特别是与垃圾收集相关的方面。
|
LinkedBlockingQueue特点
和ArrayBlockingQueue一样,Linked标识着其链表的实现。其他LinkedBlockingQueue主要特点如下:
双锁队列(非公平锁)
较于ArrayBlockingQueue,LinkedBlockingQueue的
ReentrantLock有两个——
takeLock
与
putLock
,因此,较于ArrayBlockingQueue的一个锁读写互斥,LinkedBlockingQueue两个锁独立工作,支持同时读写,可以减少锁竞争,提高多线程环境下性能。
LinkedBlockingQueue与ArrayBlockingQueue的
ReentrantLock都是默认构造的非公平锁NonfairSync,意味着它不会严格按照线程请求的顺序来分配资源。
。
可定大小边界的单向链表
较于ArrayBlockingQueue数组大小固定,LinkedBlockingQueue大小也是固定的,默认大小为Integer.MAX_VALUE。
且通过count与
capacity属性控制大小界限。
生产者消费者模型
LinkedBlockingQueue 为生产者和消费者提供了不同的阻塞策略。当队列为空时,从队列中取元素的线程(消费者)将被阻塞;当队列满时,向队列中添加元素的线程(生产者)将被阻塞。
LinkedBlockingQueue结构与属性
其中头节点item=null是构造时指定的
LinkedBlockingQueue核心方法
put-添加元素(阻塞)
在此队列的尾部插入指定的元素,必要时等待空间可用
如果之前是空的,现在队列put进元素,通知等待线程继续操作
offer方法
较put方法增加等待时间
take-获取元素(阻塞)
检索并删除此队列的头部,必要时等待,直到元素可用
dequeue最终效果:将head的下一个元素item返回。head本身回收,head的下一个元素变为head
如果之前队列是满的,现在take一个了,通知之前的等待的继续操作
遍历
以contains方法距离,看一下LinkedBlockingQueue中遍历是怎么做的
remove移除
需要注意的是,unlink更改了next,但是p.next本身没有发生变化。这样做可以确保遍历该节点的迭代器能够维持其弱一致性保证。
当没有其他节点引用p的时候, p 变成不可达对象,即会在合适的时候被回收。