compareAndSet():
public final boolean compareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}
compareAndSwapInt() 已经是原生的:
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
UnsafeWrapper("Unsafe_CompareAndSwapInt");
oop p = JNIHandles::resolve(obj);
jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END
其中Atomic::cmpxchg 是generated 在JVM 执行开始时的某处
address generate_atomic_cmpxchg() {
StubCodeMark mark(this, "StubRoutines", "atomic_cmpxchg");
address start = __ pc();
__ movl(rax, c_rarg2);
if ( os::is_MP() ) __ lock();
__ cmpxchgl(c_rarg0, Address(c_rarg1, 0));
__ ret(0);
return start;
}
cmpxchgl() 生成 x86 代码(它也有更长的遗留代码路径,所以我不在这里复制那个):
InstructionMark im(this);
prefix(adr, reg);
emit_byte(0x0F);
emit_byte(0xB1);
emit_operand(reg, adr);
0F B1 真的是CMPXCHG 操作。如果你检查上面的代码,if ( os::is_MP() ) __ lock(); 在多处理器机器上会发出一个LOCK 前缀(让我跳过引用lock(),它会发出一个F0 字节),所以几乎无处不在。
正如CMPXCHG 文档所说:
该指令可以与 LOCK 前缀一起使用,以允许指令以原子方式执行。为了简化与处理器总线的接口,目标操作数接收一个写周期,而不考虑比较结果。如果比较失败,则写回目标操作数;否则,源操作数被写入目标。 (处理器不会在不产生锁定写入的情况下产生锁定读取。)
因此,在多处理器 x86 机器上,NOP-CAS 也会进行写入,从而影响高速缓存行。 (重点是我加的)