背景
我们知道 volatile 关键字虽然轻量级,但不能保证原子性,synchronized 可以保证原子性,但是比较重量级。
那么有没有一种简单的、性能高的方法来保证 Java 的原子操作呢?答案当然是有的。
在 JDK1.5 时期, Java 家族加入了 Atomic 包。
原子类(Atomic Classes)
Java 中提供了对应的原子操作类来实现原子地且有条件进行的读-改-写操作操作。其本质是利用了 CPU 级别的原子指令。由于原子类是基于 CPU 级别的指令,因此其开销比需要操作系统参与的锁的开销小。
CAS 算法
CAS (比较与交换,Compare and swap) 算法是一种非阻塞算法(non-blocking algorhithm),同时也是一种无锁算法(lock-free algorhithm),基于乐观并发(pessimistic concurrent control)的思想。
非阻塞版本相对于基于锁的版本有几个性能优势。首先,它用硬件的原生形态代替 JVM 的锁定代码路径,从而在更细的粒度层次上(独立的内存位置)进行同步,失败的线程也可以立即重试,而不会被挂起后重新调度。更细的粒度降低了争用的机会,不用重新调度就能重试的能力也降低了争用的成本。即使有少量失败的 CAS 操作,这种方法仍然会比由于锁争用造成的重新调度快得多。
以 Atomic 类中的 incrementAndGet()
方法为例,其内部就调用了Unsafe中的 native 方法(CompareAndSet)以实现递增数值:
private volatile int value;
public final int get() {
return value;
}
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
我们来分析下incrementAndGet的逻辑:
- 先获取当前的value值
- 对value加一
- 第三步是关键步骤,调用compareAndSet方法来进行一个原子更新操作,这个方法的语义是:先检查当前value是否等于current,如果相等,则意味着value没被其他线程修改过,更新并返回true。如果不相等,compareAndSet则会返回false,然后循环继续尝试更新。
...