1 ConcurrentLinkedQueue概述
在并发编程中,我们经常使用线程安全的队列,Java为此提供了两种模式的队列:一个以ConcurrentLinkedQueue为代表的高性能队列 ;一个是以BlockingQueue接口为代表的阻塞队列。
阻塞队列使用一把锁(入队和出队共享一把锁)或者两把锁(入队、出队各一把锁)来实现线程安全,BlockingQueue是典型的代表;
非阻塞队列使用循环CAS操作保证数据的一致性,达到线程安全。
2 Node节点
ConcurrentLinkedQueue的底层数据结构是Node
private static class Node<E> {
volatile E item;
volatile Node<E> next;
.
.
.
.
}
Node节点主要包括数据域E和next指针。两者都用volatile关键字修饰,保证了内存中的可见性。
private transient volatile Node<E> head;//头结点
private transient volatile Node<E> tail;//尾节点
ConcurrentLinkedQueue还有这两个Node变量,说明使用头尾指针管理队列。
3 CAS操作
在多线程的环境下,对队列进行操作的时候难免会出现线程安全的问题;ConcurrentLinkedQueue保证线程安全的措施是对Node节点使用CAS操作
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);
}
//更改数据E
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);
}
}
}
offer 操作
public boolean offer(E e) {
checkNotNull(e);//空指针判断
//新建一个节点
final Node<E> newNode = new Node<E>(e);
//没有循环结束条件,循环执行队尾插入(tail可能指向尾节点的前一个节点)
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
if (q == null) {
// 如果tail节点是尾节点,设置p节点的next节点为待入队的节点
if (p.casNext(null, newNode)) {
//在循环体CAS操作成功会直接return返回,如果CAS操作失败的话就 在
for循环中不断重试直至成功
casTail(t, newNode); // Failure is OK.
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;
}
}
public E poll() {
restartFromHead:
for (;;) {
for (Node<E> h = head, p = h, q;;) {//获取头结点
E item = p.item;
//如果头结点不为null 同时设置头结点数据为空成功
if (item != null && p.casItem(item, null)) {
// Successful CAS is the linearization point
// for item to be removed from this queue.
if (p != h) // 如果p不等于h,说明表头发生改变,更新头结点
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;
}
}
}