繁体   English   中英

再次设置AtomicBoolean

[英]Setting an AtomicBoolean again

我使用AtomicBoolean来强制线程之间的volatile可见性。 一个线程正在更新该值,另一个线程只读取它。

说当前值为true 现在说写线程再次将其值设置为true

final AtomicBoolean b = new AtomicBoolean(); // shared between threads

b.set(true);
// ... some time later
b.set(true);

在'dummy' set(true) ,当读取线程调用get()时是否存在性能损失? 读线程是否必须重新读取并缓存该值?

如果是这种情况, 写线程可能已经完成:

b.compareAndSet(false, true);

这样,只读取线程必须使实际更改无效。

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

在JVM执行开始时某处生成 Atomic::cmpxchg的地方

  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也会执行写操作,从而影响缓存行。 (我强调了重点)

写入和CAS“触摸”高速缓存行,触发高速缓存行变脏。

然而,成本相对较小,约为30-50ns。

由于代码尚未运行10,000次而未被预热的代码成本可能会大得多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM