java并发-18-ConcurrentLinkedList设计

分类: java |
一、
在并发编程中,有时需要使用线程安全的队列。实现一个线程安全的队列有两种方式:一是使用阻塞方式,在入队、出队、读取加锁;另一种可以使用非阻塞方式,即利用CAS。Doug Lee就使用了非阻塞方式设计了ConcurrentLinkedList,类图如下:
http://s15/mw690/002i2qf8zy7jTyRs7Cude&690
ConcurrentLinkedQueue由head节点和tail节点组成,每个节点(Node)由节点元素(item)和指向下一个节点(next)的引用组成,节点与节点之间就是通过这个next关联起来,从而组成一张链表结构的队列。默认情况下head节点存储的元素为空,tail节点等于head节点。
下面为了简化,我们自己写了一个类似的队列。
private static class MyConcurrentLinkedQueue {
private transient volatile MyNodehead;
try {
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
Class<?> k =
MyConcurrentLinkedQueue.class;
headOffset= unsafe.objectFieldOffset
tailOffset= unsafe.objectFieldOffset
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
MyConcurrentLinkedQueue() {
head = tail = new MyNode(null);
}
public void setFirstTail(MyNode node) {
tail.casNext(null, node);
System.out.println();
}
final void updateHead(MyNode h, MyNode p) {
if (h != p &&casHead(h, p))
h.lazySetNext(h);
}
public booleancasHead(MyNodecmp, MyNodeval) {
return unsafe.compareAndSwapObject(this, headOffset, cmp, val);
}
public booleancasTail(MyNodecmp, MyNodeval) {
return unsafe.compareAndSwapObject(this, tailOffset, cmp, val);
}
public booleanoffer(String e) {
final MyNodenewNode = new MyNode(e);
MyNode q = p.next;
// p is last node
if (p.casNext(null, newNode)) {
// Successful CAS is the linearization point
if(p != t) // hop two nodes at a time
casTail(t, newNode);
return true;
}
// Lost CAS race to another thread; re-read
next
}
else if (p == q)
// We have fallen off list.
p = (t != (t = tail)) ? t :head;
// Check for tail updates after two
hops.
p = (p != t && t != (t =
tail)) ? t : q;
}
public String peek() {
restartFromHead:
for (;;) {
for(MyNode h = head, p = h, q;;) {
updateHead(h, p);
}
else if (p == q)
continue restartFromHead;
p = q;
}
}
private static class MyNode {
private volatile String item;
MyNode(String item) {
unsafe.putObject(this, itemOffset, item);
}
public booleancasNext(MyNodecmp, MyNodeval) {
return unsafe.compareAndSwapObject(this, nextOffset, cmp, val);
}
void lazySetNext(MyNodeval) {
unsafe.putOrderedObject(this, nextOffset, val);
}
public void setNext(MyNode next) {
this.next= next;
}
public void setItem(String s) {
item = s;
}
public String getItem() {
return item;
}
static {
try {
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
Class<?> k =
MyNode.class;
itemOffset= unsafe.objectFieldOffset(k.getDeclaredField("item"));
nextOffset= unsafe.objectFieldOffset(k.getDeclaredField("next"));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
二、
http://s7/mw690/002i2qf8zy7jTySnnYa96&690
@Test
public void test() {
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
queue.add("1");
System.out.println(queue.peek());
queue.add("2");
}
@Test
public void testSimulatedMyLinkedQueue () throws NoSuchFieldException, IllegalAccessException {
MyConcurrentLinkedQueuemyLinkedQueue = new MyConcurrentLinkedQueue();
myLinkedQueue.offer("1");
System.out.println(myLinkedQueue.peek());
myLinkedQueue.offer("2");
System.out.println();
}
所以我们在testSimulatedMyLinkedQue
三、
http://s4/mw690/002i2qf8zy7jTyT1gpdc3&690
从图中可知,并不是每次出队时都更新head节点,当head节点里有元素时,直接弹出head 节点里的元素,而不会更新head节点。只有当head节点里没有元素时,出队操作才会更新head 节点。这种做法也是通过hops变量来减少使用CAS更新head节点的消耗,从而提高出队效率。