Java 再次设置原子布尔值
我使用Java 再次设置原子布尔值,java,multithreading,caching,concurrency,volatile,Java,Multithreading,Caching,Concurrency,Volatile,我使用AtomicBoolean来强制线程之间的volatile可见性。一个线程正在更新该值,另一个线程只读取该值 假设当前值为true。现在,假设一个写线程再次将其值设置为true: final AtomicBoolean b = new AtomicBoolean(); // shared between threads b.set(true); // ... some time later b.set(true); 在这个“伪”set(true)之后,当读取线程调用get()时,是否存
AtomicBoolean
来强制线程之间的volatile
可见性。一个线程正在更新该值,另一个线程只读取该值
假设当前值为true
。现在,假设一个写线程再次将其值设置为true
:
final AtomicBoolean b = new AtomicBoolean(); // shared between threads
b.set(true);
// ... some time later
b.set(true);
在这个“伪”set(true)
之后,当读取线程调用get()
时,是否存在性能损失?读取线程是否必须重新读取和缓存该值
如果是这种情况,写入线程可以执行以下操作:
b.compareAndSet(false, true);
通过这种方式,读取线程只需对真正的更改无效。写入和CAS都会“触摸”缓存线,从而触发缓存线变脏
然而,成本相对较小,约为30-50纳秒
因为代码还没有运行10000次而没有预热的成本可能要高得多。:
已经是本地的:
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
位于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;
}
生成x86代码(它还有一个较长的遗留代码路径,因此我不在这里复制该路径):
0F
B1
实际上是一个操作。如果您检查上面的代码,If(os::is\u MP())\u\u锁定()
在多处理器机器上发出一个锁
前缀(让我跳过引用,它发出一个F0
字节),因此几乎无处不在
正如CMPXCHG
docs所说:
此指令可与锁前缀一起使用,以允许以原子方式执行该指令。为了简化与处理器总线的接口,目标操作数接收写入周期,而不考虑比较结果。如果比较失败,则写回目标操作数;否则,源操作数将写入目标。(处理器在不产生锁定写入的情况下不会产生锁定读取。)
因此,在多处理器x86机器上,NOP-CAS也会执行写操作,从而影响缓存线。(我添加了强调)为什么no-op
CAS
也会触及缓存线?例如,compareAndSet(true,true)
@OhNoOh它可以,但在英特尔x64上,它似乎不可以。例如,我们使用堆外内存上的compareAndSet(0L,0L)来拉入一个新页而不改变它。i、 e.预先触摸它,以减少按需创建页面的影响。感谢您的参考+1谢谢,非常详细。TL;DR结论:b.set(true)
或b.compareAndSet(false,true)
对读取线程没有任何影响。在这两种情况下,它都必须重新加载该值。
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;
}
InstructionMark im(this);
prefix(adr, reg);
emit_byte(0x0F);
emit_byte(0xB1);
emit_operand(reg, adr);