类的描述
public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> implements Queue<E>, Serializable
一个基于链表的无界线程安全队列。此队列对元素 FIFO(先进先出)进行排序。队列的头部是在队列中停留时间最长的那个元素。队列的尾部是在队列中停留时间最短的那个元素。新元素插入队列尾部,队列检索操作获取队列头部元素。当许多线程将共享对公共集合的访问时, ConcurrentLinkedQueue 是合适的选择。与大多数其他并发集合实现一样,此类不允许使用空元素。
该实现采用了一种高效的非阻塞算法 , 该算法基于 Maged M. Michael 和 Michael L. Scott 在 Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithmsz
所描述的算法。
迭代器是弱一致的,返回元素反映了在迭代器创建时或之后的某个时间点的队列状态。它们不会抛出 java.util.ConcurrentModificationException,并且可能与其他操作同时进行。自迭代器创建以来包含在队列中的元素将只返回一次。
请注意,与大多数集合不同,size 方法不是恒定时间操作。由于这些队列的异步特性,确定当前元素的数量需要遍历元素,因此如果在遍历期间修改此集合,则可能会报告不准确的结果。此外,批量操作 addAll、removeAll、retainAll、containsAll、equals 和 toArray 不能保证以原子方式执行。例如,与 addAll 操作同时运行的迭代器可能只查看一部分添加的元素。
这个类和它的迭代器实现所有的可选方法的Queue和Iterator接口。
内存一致性影响:与其他并发集合一样,在将对象放入 ConcurrentLinkedQueue 之前线程中的操作发生在另一个线程中从 ConcurrentLinkedQueue 访问或删除该元素之后的操作之前
常量及变量
//头节点
private transient volatile Node<E> head;
//尾节点
private transient volatile Node<E> tail;
//volatile 可以保证变量的内存可见性,关于这个我会放到多线程里面去研究
private static class Node<E> {
volatile E item;
volatile Node<E> next;
/**
* Constructs a new node. Uses relaxed write because item can
* only be seen after publication via casNext.
*/
Node(E item) {
UNSAFE.putObject(this, itemOffset, item);
}
boolean casItem(E cmp, E val) {
return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
void lazySetNext(Node<E> val) {
UNSAFE.putOrderedObject(this, nextOffset, val);
}
boolean casNext(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long itemOffset;
private static final long nextOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = Node.class;
itemOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("item"));
nextOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("next"));
} catch (Exception e) {
throw new Error(e);
}
}
}
构造方法
//默认构造函数, 头节点赫尾节点是用一个节点,节点内item 尾null
public ConcurrentLinkedQueue() {
head = tail = new Node<E>(null);
}
//实例化一个指定集合元素的队列
public ConcurrentLinkedQueue(Collection<? extends E> c) {
Node<E> h = null, t = null;
for (E e : c) {
checkNotNull(e); //队列元素不能为空,所以有空就报空指针异常
Node<E> newNode = new Node<E>(e);
if (h == null)
h = t = newNode; //如果hnull,说明根据当前集合c生成的链表是空的,所以指定
//头节点和尾节点都是当前节点
else {
t.lazySetNext(newNode); //这里是调用Unsafe.putOrderedObject, 作用就是t的下 //一个节点设置未newNode
t = newNode;
}
}
if (h == null)
h = t = new Node<E>(null);
head = h;
tail = t;
}
void lazySetNext(Node<E> val) {
//为什么这里使用UNSAFE.putOrderedObject 去写,我上网收了下,感觉都理解不了,所以我
//把这个问题遗留在这里,等研究UNSAFE时在解决,如果有知道的同学,请在评论里留言
UNSAFE.putOrderedObject(this, nextOffset, val);
}
通过构造方法和变量我们可以理解和确认类的描述中讲的 ConcurrentLinkedQueue是通过单向链表来实现的
队列的常用的方法
add
//单个元素添加,调用iffer
public boolean add(E e) {
return offer(e);
}
//批量添加
public boolean addAll(Collection<? extends E> c) {
if (c == this)
// As historically specified in AbstractQueue#addAll
throw new IllegalArgumentException();
// Copy c into a private chain of Nodes
Node<E> beginningOfTheEnd = null, last = null;
for (E e : c) {
checkNotNull(e); //判断元素是否为空
Node<E> newNode = new Node<E>(e);
if (beginningOfTheEnd == null)
beginningOfTheEnd = last = newNode;
else {
//设置last的下一个节点是newNode, 然后newNode变为last
last.lazySetNext(newNode);
last = newNode;
}
}
if (beginningOfTheEnd == null) //如果beginningOfTheEnd == null 说明c的size为0
return false;
// Atomically append the chain at the tail of this collection
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
if (q == null) {
// p is last node
if (p.casNext(null, beginningOfTheEnd)) {
// Successful CAS is the linearization point
// for all elements to be added to this queue.
if (!casTail(t, last)) {
// Try a little harder to update tail,
// since we may be adding many elements.
t = tail;
if (last.next == null)
casTail(t, last);
}
return true;
}
// Lost CAS race to another thread; re-read next
}
else if (p == q)
// We have fallen off list. If tail is unchanged, it
// will also be off-list, in which case we need to
// jump to head, from which all live nodes are always
// reachable. Else the new tail is a better bet.
p = (t != (t = tail)) ? t : head;
else
// Check for tail updates after two hops.
p = (p != t && t != (t = tail)) ? t : q;
}
}
boolean casNext(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
private boolean casTail(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
}
offer
public boolean offer(E e) {
checkNotNull(e);
final Node<E> newNode = new Node<E>(e);
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
if (q == null) { //通过cas进行插入node
// p is last node
if (p.casNext(null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this queue,
// and for newNode to become "live".
if (p != t) // hop two nodes at a time
casTail(t, newNode); // Failure is OK.
return true;
}
// Lost CAS race to another thread; re-read next
}
else if (p == q) // 如果在添加前另一个线程进行了remove会导致这种情况
// We have fallen off list. If tail is unchanged, it
// will also be off-list, in which case we need to
// jump to head, from which all live nodes are always
// reachable. Else the new tail is a better bet.
p = (t != (t = tail)) ? t : head;
else
// Check for tail updates after two hops.
p = (p != t && t != (t = tail)) ? t : q;
}
}
remove
public boolean remove(Object o) {
if (o != null) {
Node<E> next, pred = null;
for (Node<E> p = first(); p != null; pred = p, p = next) {
boolean removed = false;
E item = p.item;
if (item != null) {
if (!o.equals(item)) {
next = succ(p);
continue;
}
removed = p.casItem(item, null);
}
next = succ(p);
if (pred != null && next != null) // unlink
pred.casNext(p, next);
if (removed)
return true;
}
}
return false;
}
poll
public E poll() {
restartFromHead:
for (;;) {
for (Node<E> h = head, p = h, q;;) {
E item = p.item;
if (item != null && p.casItem(item, null)) {
// Successful CAS is the linearization point
// for item to be removed from this queue.
if (p != h) // hop two nodes at a time
updateHead(h, ((q = p.next) != null) ? q : p);
return item;
}
else if ((q = p.next) == null) {
updateHead(h, p);
return null;
}
else if (p == q)
continue restartFromHead;
else
p = q;
}
}
}
关于offer和poll的深入了解,请看这篇文章。 文章中讲的很细,只要跟着作者的思路一步一步去走可以弄明白
peek
public E peek() {
restartFromHead:
for (;;) {
for (Node<E> h = head, p = h, q;;) {
E item = p.item;
if (item != null || (q = p.next) == null) {
updateHead(h, p);
return item;
}
else if (p == q)
continue restartFromHead;
else
p = q;
}
}
}
element
//AbstractQueue 的element
public E element() {
E x = peek();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
结论:
1. ConcurrentLinkedQueue是通过Node来存储元素的
2. 数据结构是单项链表, 所以是单向队列
3、 ConcurrentLinkedQueue 是通过Unsafe的cas乐观锁和volitile修饰head和tail来实现线程安全的
本文详细介绍了ConcurrentLinkedQueue,一种无界线程安全队列,利用链表结构和非阻塞算法实现FIFO排序。讲解了构造方法、内存可见性与线程安全机制,以及核心操作如add、offer、remove和poll的原理。
https://blog.youkuaiyun.com/u011521203/article/details/80214968

1857

被折叠的 条评论
为什么被折叠?



