系列文章目录
线程安全(一)java对象头分析以及锁状态
线程安全(二)java中的CAS机制
线程安全(三)实现方法sychronized与ReentrantLock(阻塞同步)
线程安全(四)Java内存模型与volatile关键字
线程安全(五)线程状态和线程创建
线程安全(六)线程池
线程安全(七)ThreadLocal和java的四种引用
线程安全(八)Semaphore
线程安全(九)CyclicBarrier
线程安全(十)AQS(AbstractQueuedSynchronizer)
线程安全(十一)ConcurrentLinkedQueue的原理以及CAS机制在其中的应用
0.前言
ConcurrentLinkedQueue是一个线程安全队列,区别于阻塞算法的锁机制,它使用了基于CAS机制的非阻塞算法。
1.队列
队列:通常(但并非一定)以 FIFO(先进先出)的方式排序各个元素。
接口Queue
接口quque中的方法可以分为三类
添加队列尾元素
boolean add(E e)与boolean offer(E e)
add结果只能为返回true(成功插入)或抛出异常(不成功),根据异常类型(IllegalStateException)才能看出是否由于对列容量限制
offer结果返回true(成功插入)或返回false(容量限制)或抛出异常
获取队列头但不移除
E element() 与 E peek()
区别在于当队列为空时 elemnet()方法抛出异常
peek方法返回null
获取对列头并移除
remove() 与 poll()
区别在于队列为空时 remove()方法抛出异常
poll方法返回null
2.CAS机制
之前文章说明过CAS机制的依据(硬件层面的是实现):http://blog.csdn.net/do_finsh/article/details/78773573
以及大体java.util.concurrent包下的类实现的共同点:
- .声明共享变量为volatile;(没有原子性,有可见性)
- 使用CAS的原子条件更新来实现线程之间的同步;
- 配合以volatile的读/写和CAS所具有的volatile读和写的内存语义来实现线程之间的通信。
现在具体说明一下ConcurrentLinkedQueue中的应用
首先出现了一个私有静态类Node
这个Node类中出现了UNSAFE类,节点类Node< E> 通过UNSAFE类的方法,进行CAS操作。大致了解一下
UNSAFE类的方法
// 获取f在堆内存的偏移地址
public native long objectFieldOffset(Field f);
// 获取静态f在堆内存的偏移地址
public native long staticFieldOffset(Field f);
// 原子的更改offset地址的变量。如果变量的值为expected,并成功替换为x 返回true CAS操作
public final native boolean compareAndSwapObject(Object o, long offset, Object expected,Object x);
// 将offset地址的值替换为x,并且通知其他线程。因为有Volatile,与putObject类似
public native void putObjectVolatile(Object o, long offset, Object x);
// 获取地址为offset 的值
public native Object getObjectVolatile(Object o, long offset)
/**
* Version of {@link #putObjectVolatile(Object, long, Object)}
* that does not guarantee immediate visibility of the store to
* other threads. This method is generally only useful if the
* underlying field is a Java volatile (or if an array cell, one
* that is otherwise only accessed using volatile accesses).
*/
public native void putOrderedObject(Object o, long offset, Object x);
节点类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) {
UNSAFE.putOrderedObject(this, nextOffset, val);
}
// CAS操作:比较并交换,原子的更改nextOffset地址的变量。如果变量的值为cmp,并成功替换为val 返回true
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;
/**
* 静态代码块,获取itemOffset和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);
}
}
}
3.源码解析
ConcurrentLinkedQueue的常用方法offer(E e);
public boolean offer(E e) {
checkNotNull(e);
//创建一个入队节点
final Node<E> newNode = new Node<E>(e);
// 死循环,直到return ture
// 创建一个指向tail节点的引用t
// p用来表示尾结点
for (Node<E> t = tail, p = t;;) {
// 获取p的下一节点
Node<E> q = p.next;
// 如果q为空,说明p是尾节点
if (q == null) {
// p是尾节点,把e放入p节点之后,CAS操作(原子性操作),成功,跳出循环
if (p.casNext(null, newNode)) {
if (p != t)
// 如果tail不是尾节点则将入队节点设置为tail。如果失败了,那么说明有其他线程已经把tail移动过
casTail(t, newNode);
return true;
}
}
// 如果p节点等于p的next节点,则说明p节点和q节点都为空,表示队列刚初始化,所以返回
else if (p == q)
p = (t != (t = tail)) ? t : head;
else
//p有next节点,表示p的next节点是尾节点,则需要重新更新p后将它指向next节点
//此步判断非原子性操作t != (t = tail)
p = (p != t && t != (t = tail)) ? t : q;
}
}