您的位置:首页 > 编程语言 > Java开发

jdk concurrent collection---ConcurrentLinkedQueue原理分析

2009-11-30 11:36 633 查看



先来回顾之前提到过的ConcurrentHashMap,它是一个以Concurrent开头的并发集合类,其原理是通过增加锁和细化锁的粒度来提高并发度。

  另一个值得一提的Concurrent是ConcurrentLinkedQueue。这个类采用了另一种提高并发度的方式:非阻塞算法(Non-blocking),第一次实现了无锁的并发。

 
谈到这里,先要介绍一下非阻塞算法。其实非阻塞算法并不是什么神秘高深的东西,它需要有一套硬件和指令的配合(似乎目前大多数pc都能支持),主要解决的
问题是:在许多时候,一个线程A持有其他线程B,C,D所需要的资源,但线程A遭遇网络阻塞,或数据库连接阻塞,或页面阻塞等。这时B,C,D就必须等待
A执行结束才能继续向前推进。这种情况在队列、堆栈等数据结构中也会经常出现,典型的如将一个队列中的数据取出来需要锁整个队列,也就是说,在对队列的操
作中,各个线程实际上是串行的,中间还需要加上线程上下文切换的开销。如何在取队列中元素时进一步提高并发度(就像ConcurrentHashMap一
样只锁部分)。ConcurrentHashMap是固定的16个段,并且每个段的操作是独立的,所以每个段使用了一把锁,关于这点也是考虑到一些开销和
安全的问题,而队列中元素则是可以动态增长的,因为要涉及到队列指针的问题,不是锁单独一个元素就能够保证其原子性的。这时传说中的非阻塞算法就是比较好
的选择了。
非阻塞算法



  在《Concurrency in
practice》中对两个概念nonblocking和lock-free进行了解释。nonblocking定义为:任何线程失败或挂起不影响其他线
程的失败或挂起;而lock-free定义为:在执行的的每一步,都有线程能够向前推进。而一个基于CAS(compareAndSet)且构造正确的算
法一定是nonblocking和lock-free的。
 
对于java中的非阻塞算法,核心原理是采用硬件级的指令来保证CAS的原子性,不同于lock这样的悲观锁定,非阻塞算法是乐观的,它基于某些算法步骤
是不安全的,在每次进行CAS时可能成功,也可能失败,失败则再取新值重新CAS,这样不用每次使用lock以保证得到锁的线程必须成功。
  一个比较好的例子是Java 理论与实践: 非阻塞算法简介
中的Nonblocking
stack,这里采用的是Treiber
的非阻塞算法。这个例子比较容易,之后有一个对ConcurrentLinkedQueue的put方法介绍的例子,这里又是采用的Michael-Scott算法

 

开发非阻塞算法是一项非常有挑战的任务,对一个算法中的每一步都需要证明不会产生冲突和死锁。当然,也遵循一些规律,首先无论是否在多线程的多步执行中必
须使得数据结构总是在一致的状态。即一个线程不能打断另一个线程的原子操作。其次,假设一个线程执行更新,另一个线程等待更新,如果前一个线程更新失败,
则后一个线程会浪费等待时间,并且在等待中没有任何向前推进。解决的办法是细化原子操作的粒度,并且后一个线程使用快照。
 @ThreadSafe

public class LinkedQueue <E> {

private static class Node <E> {

    final E
item;

    final
AtomicReference<Node<E>> next;

   
public Node(E item, Node<E> next) {
       

this.item = item;
       

this.next = new AtomicReference<Node<E>>(next);
   
}

}

private final Node<E> dummy = new Node<E>(null,
null);

private final AtomicReference<Node<E>> head

= new AtomicReference<Node<E>>(dummy);

private final AtomicReference<Node<E>> tail

= new AtomicReference<Node<E>>(dummy);

public boolean put(E item) {
   
Node<E> newNode = new Node<E>(item, null);
   
while (true) {
   
Node<E> curTail = tail.get();
   
Node<E> tailNext = curTail.next.get();
   
if (curTail == tail.get()) {
       

if (tailNext != null) {
           

tail.compareAndSet(curTail, tailNext);
       

} else {
    
       

if (curTail.next.compareAndSet(null, newNode)) {
               

tail.compareAndSet(curTail, newNode);
               

return true;
           

}
    
   
}
   
}
   
}

}

}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息