这个同步块需要吗?

时间:2016-05-06 08:01:21

标签: java multithreading

System.out.println(number);上的同步块是否需要以下代码?

import java.util.concurrent.CountDownLatch;

public class Main {

    private static final Object LOCK = new Object();

    private static long number = 0L;

    public static void main(String[] args) throws InterruptedException {

        CountDownLatch doneSignal = new CountDownLatch(10);

        for (int i = 0; i < 10; i++) {
            Worker worker = new Worker(doneSignal);
            worker.start();
        }

        doneSignal.await();

        synchronized (LOCK) { // Is this synchronized block need?
            System.out.println(number);
        }
    }

    private static class Worker extends Thread {

        private final CountDownLatch doneSignal;

        private Worker(CountDownLatch doneSignal) {
            this.doneSignal = doneSignal;
        }

        @Override
        public void run() {
            synchronized (LOCK) {
                number += 1;
            }
            doneSignal.countDown();
        }

    }
}

我认为这是必要的,因为有可能读取缓存的值。

但有人说:

没必要 因为当主线程读取变量number时,所有工作线程都在变量number的内存中完成了写操作。

5 个答案:

答案 0 :(得分:5)

doneSignal.await()是一个屏蔽通话,因此只有当您的所有main()主题调用Worker时,doneSignal.countDown()才会进行,使其达到0,这就是await()方法返回。

synchronized之前添加System.out.println()块没有意义,所有线程都已在此时完成。

考虑对AtomicInteger使用number,而不是与锁定同步来调用+= 1

答案 1 :(得分:1)

没有必要:

CountDownLatch doneSignal = new CountDownLatch(10);

for (int i = 0; i < 10; i++) {
  Worker worker = new Worker(doneSignal);
  worker.start();
}
doneSignal.await();
// here the only thread running is the main thread

在死亡之前每个线程countDown countDownLatch

@Override
public void run() {
  synchronized (LOCK) {
    number += 1;
  }
  doneSignal.countDown();
}

只有当10个线程完成其工作时,才能完成doneSignal.await();线将超越。

答案 2 :(得分:1)

没有必要,因为您正在等待“完成”信号。刷新内存的方式是等待线程的所有值都变为主线程可见。

然而,您可以轻松地测试它,在run方法内部进行一项需要几(百万)步的计算,并且如果您看到的值与最终值不同,则不会被编译器优化你希望你的最终价值对于主线程是不可见的。当然,这里的关键部分是确保计算不会得到优化,因此简单的“增量”可能会得到优化。这通常对测试并发性非常有用,因为你不确定你是否有正确的内存障碍,以便以后可能会对你有用。

答案 3 :(得分:1)

synchronized周围不需要

System.out.println(number);,但不是因为PrintWriter.println()实现是内部同步的,或者因为doneSignal.await()解锁所有工作线程已经完成。< / p> 不需要

synchronized,因为在每次调用doneSignal.countDown之前,所有内容之间都有发生边缘,并且doneSignal.await()完成。这可以保证您能够成功查看number的正确值。

答案 4 :(得分:-1)

<强>所需

没有。

但是,由于没有(记录)保证不存在任何交错,因此可能找到交错的日志条目。

System.out.println("ABC");
System.out.println("123");

可以打印:

AB1
23C

<强>是否值得

几乎肯定不是。大多数JVM将使用锁open JDK does实现println

边缘情况

正如@DimitarDimitrov所建议的那样,还有一个可能用于锁定的方法,即确保在访问number时跨越内存屏障。如果这是问题,那么您不需要lock,您只需要number volatile

private static volatile long number = 0L;