title: 深度分析ConcurrentLinkedQueue原理
date: 2019-04-23 20:19:04
categories:
- Java并发
tags: - 并发容器
引言
在JUC
包中提供了许多线程安全的并发容器,使用这些容器,无需我们再去手动的设置锁,就能实现线程安全。在这些并发容器中,又分为阻塞与非阻塞(简单来说线程进行元素的放置与取出操作时,可以将线程阻塞)。ConcurrentLinkedQueue
就是以非阻塞方式来实现的,这个并发容器类通过循环CAS
操作来实现线程安全队列,它不会导致当前线程被暂停,因此也避免了线程上下文切换的开销。
CAS的原理
CAS
(Compare and Swap):比较并交换。它是对一种处理器指令的称呼,在Java
中,CAS
通过调用JNI
的代码实现的。CAS
操作顾名思义,先比较再交换(或者说更新),通过一段伪代码来看:
for(;;){
...
public boolean cas(V a, V b ,V c){
//a为想要修改的变量,b为当前线程调用CAS操作时a的值(即a的旧值),c为新值
if(b == a){
//检查其他线程是否已经对a进行了修改
b = c; //更新
return true; //更新成功
}
return false; //更新操作
}
...
}
当某个线程要执行
CAS
操作时,如果想要修改的值与调用CAS
操作的线程所提供的旧值相同时,说明其他线程并未对其进行修改,则这个线程可以对其进行修改。而其他的线程则更新失败,然后继续进行尝试,直至成功。
ConcurrentLinkedQueue原理
简单来说,ConcurrentLinkedQueue
就相当于LinkedList
的线程安全版,即是一个单向链表,在其内部有个私有静态类Node<E>
,通过这个类,将元素封装在其中,并保存下一个节点(next
),Node<E>
内部通过一个UNSAFE
类来完成CAS
操作。
Node类的主要代码:
private static class Node<E> {
volatile E item; // volatile声明
volatile Node<E> next; // volatile声明
// 构造方法 在地址itemOffset处, 值替换为item
Node(E item) {
UNSAFE.putObject(this, itemOffset, item);
}
// CAS操作:比较并交换,原子的更改itemOffset地址的变量。如果变量的值为cmp,并成功替换为val 返回true
boolean casItem(E cmp, E val) {
return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
// 将nextOffset地址的值替换为x
void lazySetNext(Node<E> val)