锁的劣势
使用锁有以下的缺点:
-
如果线程持有锁而延迟,会导致其他的线程的等待。
-
高优先级的线程阻塞,而低优先级的线程持有锁造成 优先级反转(priority inversion)。
-
如果持有锁的线程被永久地阻塞,所有等待这个锁的线程就无法执行下去,造成 死锁(dead lock)。
为了避免这些问题,我们更倾向于非阻塞算法来保证同步。
比较交换(CAS)
CAS的含义是:“我认为V的值应该是A,如果是,那么将V的值更新为B,否则不修改并告诉V的值实际为多少”。CAS是一项乐观锁的技术,它希望能成功地执行更新操作,并且如果有另外一个线程在最近一次检查后更新了该变量,那么CAS能检测到这个错误。
原子变量
原子变量是JVM对CAS的支持。
原子变量比锁的粒度更细,量级更轻,并且对于在多处理器系统上实现高性能的并发代码来说是非常关键的。原子变量将发生竞争的范围缩小到单个变量上,这是你获得的粒度最新的情况。
原子变量是一种“更好的volatile”。
原子变量主要包括java.util.concurrent.atomic 包下的类。
- AtomicBoolean 可以用原子方式更新的 boolean 值。
- AtomicInteger 可以用原子方式更新的 int 值。
- AtomicIntegerArray 可以用原子方式更新其元素的 int 数组。
- AtomicIntegerFieldUpdater 基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。
- AtomicLong 可以用原子方式更新的 long 值。
- AtomicLongArray 可以用原子方式更新其元素的 long 数组。
- AtomicLongFieldUpdater 基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新。
- AtomicMarkableReference AtomicMarkableReference 维护带有标记位的对象引用,可以原子方式对其进行更新。
- AtomicReference 可以用原子方式更新的对象引用。
- AtomicReferenceArray 可以用原子方式更新其元素的对象引用数组。
- AtomicReferenceFieldUpdater<T,V> 基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。
- AtomicStampedReference AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。
ABA问题
在某些算法中,如果V的值首先由A变成B,再由B变成A,那么结果看起来没变,实际上数据已经发生过变化了。
ABA问题是一种异常现象:如果在算法中的节点可以被循环使用,那么在使用“比较并交换”指令时就可能出现这种问题(主要在没有垃圾回收机制的环境中)。在CAS操作中将判断“V的值是否仍然是A?”,并且如果是的话就继续执行更新操作。在大多数情况下,这种判断是完全足够的。然而,有时候还需要知道“自从上次看到V的值为A以来,这个值是否发生了变化?”
在某些算法中,如果V的值首先由A变成B,再由B变成A,那么结果看起来没变,实际上数据已经发生过变化了。
有一个相对简单的解决方案:不是更新某个引用的值,而是更新两个值,包括一个引用和一个版本号。即使这个值由A变成 B,然后又变成A,版本号也将是不同的。