Java 倒计时锁存和进一步同步
假设我有以下类定义,当一个线程想要为多个(可能)等待线程设置一个类时:Java 倒计时锁存和进一步同步,java,multithreading,synchronization,countdownlatch,Java,Multithreading,Synchronization,Countdownlatch,假设我有以下类定义,当一个线程想要为多个(可能)等待线程设置一个类时: public class A { private int a; private CountDownLatch gate; public A(int a) { a = 1; gate = new CountDownLatch(1); } public int getA() { latch.await(); return
public class A {
private int a;
private CountDownLatch gate;
public A(int a) {
a = 1;
gate = new CountDownLatch(1);
}
public int getA() {
latch.await();
return a;
}
public void setA(int a) {
this.a = a;
gate.countDown();
}
}
在我看来,a需要是易失性的,但我不确定……有人能告诉我为什么,如果有的话,getA需要额外的同步,或者a需要是易失性的吗?根据:
在计数达到零之前,调用countDown()
之前线程中的操作发生在另一个线程中相应的wait()
成功返回后的操作之前
因此,如果只调用setA
一次,就不需要额外的同步。如果您再次调用它,因为计数已经是0,您将无法获得相同的保证
如果预期用途是只调用setA
一次,那么如果多次调用该异常以强制执行该契约,则可以抛出异常(尽管在没有额外同步的情况下,检查计数并将新值分配给原子可能很棘手)
如果您很高兴可以多次调用
setA
,那么您需要额外的同步。实际上a
不需要易变,因为倒计时()
加载并存储到AbstractQueuedSynchronizer
的volatile状态
变量中,该变量在倒计时闩锁
中使用。易失性存储触发内存屏障()。根据JMM,所有以前的存储(对于其他变量)对于其他线程都是可见的assylias是正确的,只有调用
setA()
一次才是正确的,因为您将闩锁构造为新的倒计时闩锁(1)
非常感谢!如何实现这种内存一致性?在我看来,闩锁与类A没有任何关系,无法跨多个线程同步A的状态…如何实现内存一致性?
我在回答中描述了这一点:)@Bober02所有读取线程(调用getA
)将在等待完成后读取A
(程序顺序)。写入线程(调用setA
的唯一线程)在写入a
(程序顺序)后正在调用countDown
。因为发生在关系可传递之前,javadoc中给出的保证意味着对a
的写入发生在对a
的读取之前。所有这些都假设setA
只调用一次。同意,但这仍然是高级别的。易失性写入/障碍更让我失望alley@Bober02我不知道你花了多少钱ct—在这种情况下,处理关系发生在关系之前就足够了。您可以参考处理器屏障指令,但它们不是Java内存模型的一部分(即使JVM通常使用它们来实现JMM).啊,所以在闩锁的状态中存在易失性读/写。这反过来又是一个完整的屏障,因此更改会在缓存等之间传播。正确吗?是的,确实会发生这种情况。