原子变量
锁使用起来比较方法,但是它存在一些问题:
- 性能问题。尽管现代JVM对于锁进行了很多优化,但是在多线程下,线程上下文切换仍需要os的支持,这部分开销始终是无法避免的。在计算任务比较断的时候,线程上下文切换将占据极大的部分,发生激烈的竞争,影响性能。换句话来说,锁太重了。
- 活跃性问题。一个线程失败,可能引起所有线程阻塞。
volatile显然是一种非常轻量的同步操作,它不会引起上下文切换,但它无法支持复合操作,例如i++,看起来是一条指令,实际上是三条字节码指令。所以它也无法支持“测试并更新”这种在并发世界里极为重要但复合操作。
有没有一种粒度更细,类似于volatile,又支持原子更新操作的机制呢?
原子变量运而生。它是对于常规变量的一种封装,利用底层native接口,提供了变量上的原子一系列复合操作,例如i++,CAS等。具体参考CAS和原子类。原子变量本质上是用硬件实现的“测试和更新”复合原子操作,并且,在大量精巧的非阻塞并发程序中,核心就是将一致性问题一步步缩小范围到某一组原子变量上面。
非阻塞算法设计
如果在算法的每个步骤中都存在某个线程能够执行下去,这种算法就被称为无锁(Lock-Free)算法。
不管是非阻塞算法还是无锁算法,都是针对锁的活跃性问题衍生出来的概念。这种算法通常比锁更难设计,核心就是将一致性问题一步步缩小范围到某一组原子变量上面。
那么,如何利用原理变量设计更好的非阻塞机制呢?我们来看一些案例。
非阻塞计数器:
@ThreadSafe
public class CasCounter {
private SimulatedCAS value;
public int getValue() {
return value.get();
}
public int increment() {
int v;
do{
v = value.get();
// CAS更新,避免更新丢失
} while (v != value.compareAndSwap(v, v + 1));
return v + 1;
}
}
维持复合不变性条件(涉及多个变量):
- 代码中必须保证lower<upper
- 由于涉及到两个变量,两个变量必须同时更新
- 防止丢失修改,采用CAS
public class CasNumberRange {
// 1. 保证复合不变性条件,lower和upper必须作为一个整体更新
@Immutable
private static class IntPair {
final int lower; // Invariant: lower <= upper
final int upper;
}
private final AtomicReference<IntPair> values = new AtomicReference<IntPair>(new IntPair(0, 0));
public int getLower() {
return values.get().lower;
}
public int getUpper() {
return values.get().upper;
}
public void setLower(int i) {