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);