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
的内存中完成了写操作。
答案 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;