无锁编程,即不使用锁的情况下实现多线程之间的变量同步,所以也叫非阻塞同步(Non-blocking Synchronization),这样线程就没有阻塞了。
实现非阻塞同步的方案称为无锁编程算法( Non-blocking algorithm)。
Bool CAS(T* addr, T expected, T newValue)
{
if( *addr == expected )
{
*addr = newValue;
return true;
}
else
return false;
}
自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。
当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。
在没有锁的机制下可能需要借助volatile原语,保证线程间的数据是可见的(共享的)。
现代的CPU提供了特殊的指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet() 就用这些代替了锁定。
拿出AtomicInteger来研究在没有锁的情况下是如何做到数据正确性的,来看看++i是怎么做到的。
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。其中。compareAndSet利用JNI来完成CPU指令的操作。
整体的过程就是这样子的,利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。其它原子操作都是利用类似的特性完成的。
关于ABA问题,用例子说明ABA的危害。
线程1准备用CAS将变量的值由A替换为B,在此之前,线程2将变量的值由A替换为C,又由C替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了,尽管CAS成功,但可能存在潜藏的问题,例如下面的例子:
A->B
现有一个用单向链表实现的堆栈,栈顶为A,这时线程T1已经知道A.next为B,然后希望用CAS将栈顶替换为B:
head.compareAndSet(A,B);
在T1执行上面这条指令之前,线程T2介入,将A、B出栈,再pushD、C、A,此时堆栈结构如下图,而对象B此时处于游离状态:
A->C->D
此时轮到线程T1执行CAS操作,检测发现栈顶仍为A,所以CAS成功,栈顶变为B,但实际上B.next为null,所以此时的情况变为:
BC->D
其中堆栈中只有B一个元素,C和D组成的链表不再存在于堆栈中,平白无故就把C、D丢掉了。
Java提供AtomicStampedReference/AtomicMarkableReference,这两个类俊实现了真正的原子操作。
对于AtomicStampReference,其定义
/**
* An {@code AtomicStampedReference} maintains an object reference
* along with an integer "stamp", that can be updated atomically.
*
* Implementation note: This implementation maintains stamped
* references by creating internal objects representing "boxed"
* [reference, integer] pairs.
*
* @since 1.5
* @author Doug Lea
* @param The type of object referred to by this reference
*/
public class AtomicStampedReference{ ....... }
对于AtomicMarkableReference,其定义
/**
* An {@code AtomicMarkableReference} maintains an object reference
* along with a mark bit, that can be updated atomically.
*
* Implementation note: This implementation maintains markable
* references by creating internal objects representing "boxed"
* [reference, boolean] pairs.
*
* @since 1.5
* @author Doug Lea
* @param The type of object referred to by this reference
*/
public class AtomicMarkableReference { ...... }
from:
http://blog.csdn.net/hsuxu/article/details/9467651
http://www.cnblogs.com/caca/p/lock-free_CAS_ABA.html
https://www.ibm.com/developerworks/cn/java/j-jtp11234/
http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html ABA
http://en.wikipedia.org/wiki/Non-blocking_algorithm
http://www.ibm.com/developerworks/cn/linux/l-cn-lockfree/
http://en.wikipedia.org/wiki/Load-Link/Store-Conditional : LL/SC
http://en.wikipedia.org/wiki/Compare-and-swap CAS
http://en.wikipedia.org/wiki/Optimistic_concurrency_control
http://en.wikipedia.org/wiki/ABA_problem ABA 以及相关例子
http://en.wikipedia.org/wiki/Preemption_(computing) 抢占