《Java并发编程实践》第15章 原子变量与非阻塞同步机制 读书笔记
于是,就需要类似于volatile变量的机制,并且还要支持原子化更新的技术。原子变量类就满足了这样的需求。
原子变量类共12个,分4组
AtomicInteger AtomicLong AtomicBoolean AtomicReference
AtomicIntegerFieldUpdater AtomicLongFieldUpdater AtomicReferenceFieldUpdater
AtomicIntegerArray AtomicLongArray AtomicReferenceArray
AtomicMarkableReference AtomicStampedReference
Atomic类的实现依赖于冲突监测,从而能判定更新过程中是否存在来自于其他成员的干涉,在冲突发生的情况下,操作失败,并会重试(也可能不重试)。
现代的处理器提供了原子化的读-改-写指令,如比较并交换(compare-and-swap)。CAS有3个操作数,内存位置V,旧的预期值A,新值B。当且仅当V等于旧的预期值A时,CAS用新值B原子化地更新V的值,否则什么都不会做。在任何一种情况下,都会返回V的真实值。
若处理器不支持这样的指令,JVM会使用自旋锁。
利用AtomicInteger实现i++复合操作。
AtomicInteger i = new AtomicInteger(0); i.incrementAndGet();
相关Java源码
package java.util.concurrent.atomic;
import sun.misc.Unsafe;
public class AtomicInteger extends Number implements java.io.Serializable {
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
}
incrementAndGet方法体内是一个for循环,表示如果冲突发生,就不断重试,直到compareAndSet方法返回true。
compareAndSet方法调用的是Unsafe类的compareAndSwapInt方法,该方法是一个native方法。
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
该native方法的最终实现源码路径为openjdk\hotspot\src\os_cpu\windows_x86\vm\atomic_windows_x86.inline.hpp
// Adding a lock prefix to an instruction on MP machine // VC++ doesn't like the lock prefix to be on a single line // so we can't insert a label after the lock prefix. // By emitting a lock prefix, we can define a label after it. #define LOCK_IF_MP(mp) __asm cmp mp, 0 \ __asm je L0 \ __asm _emit 0xF0 \ __asm L0: inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value) { // alternative for InterlockedCompareExchange int mp = os::is_MP();
__asm {
mov edx, dest
mov ecx, exchange_value
mov eax, compare_value
LOCK_IF_MP(mp)
cmpxchg dword ptr [edx], ecx
}
}
如源代码所示,如果是多处理器机器,LOCK_IF_MP(mp)就会为cmpxchg指令加上lock前缀。原因是有可能多个处理器同时从各自的缓存中读取共享变量,分别进行操作,然后分别写入系统内存当中。这时就需要加锁,其中一种方式就是总线锁。
CAS只检测”V的值是否仍为A“,然而可能V的变化过程是A -> B -> A,这就是ABA问题。要知道“V的值在我上次观察后是否发生变化”,一种解决方案是,更新一对值,包括引用和版本号。A改为B,又改回A,版本号不同。AtomicStampedReference或AtomicMarkableReference提供了一对变量原子化的条件更新。
AtomicStampedReference更新对象引用的整数对
AtomicMarkableReference更新对象引用的布尔对