倒计时和进一步同步

Countdownlatch and further synchronisation

假设我有以下 class 定义,当一个线程想要为多个(可能)等待的线程设置 a 时:

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 需要可变?

根据the javadoc

Until the count reaches zero, actions in a thread prior to calling countDown() happen-before actions following a successful return from a corresponding await() in another thread.

因此,如果您只调用 setA 一次,则不需要额外的同步。如果你第二次调用它,因为计数已经为 0,你将不会得到相同的保证。

如果预期的用途是只调用 setA 一次,如果多次调用它来执行该合同,您可能会抛出异常(尽管检查计数并为原子分配新值可能是没有额外的同步就很棘手)。

如果您对 setA 可以被多次调用感到高兴,那么您需要额外的同步。

实际上a不需要是volatile的,因为countDown()加载并存储到AbstractQueuedSynchronizer的volatilestate变量中,在CountDownLatch中使用.易失性存储触发 memory-barrier (great in-depth article about Memory Barriers and etc in JSR-133)。根据 JMM,所有以前的存储(对其他变量)将对其他线程可见。
assylias 是对的,只有调用一次 setA() 才会正确,因为你将 latch 构造为 new CountDownLatch(1).