Java 这是否需要同步块?
是Java 这是否需要同步块?,java,multithreading,Java,Multithreading,是System.out.println(编号)上的同步块需要以下代码吗 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 Interr
System.out.println(编号)上的同步块代码>需要以下代码吗
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();
}
}
}
import java.util.concurrent.CountDownLatch;
公共班机{
私有静态最终对象锁=新对象();
专用静态长编号=0L;
公共静态void main(字符串[]args)引发InterruptedException{
CountDownLatch doneSignal=新的CountDownLatch(10);
对于(int i=0;i<10;i++){
工人=新工人(doneSignal);
worker.start();
}
doneSignal.wait();
已同步(锁定){//是否需要此同步块?
系统输出打印项次(编号);
}
}
私有静态类工作线程扩展{
私人最终倒计时信号;
私人工人(倒计时信号){
this.doneSignal=doneSignal;
}
@凌驾
公开募捐{
已同步(锁定){
数字+=1;
}
doneSignal.countDown();
}
}
}
我认为这是必要的,因为有可能读取缓存的值
但有人说:
这是不必要的。
因为当主线程读取变量number
时,所有工作线程都在变量number
的内存中执行了写操作doneSignal.wait()
是一个阻塞调用,所以只有当所有worker
线程都调用了doneSignal.countDown()时,main()
才会继续
,使其达到0,这就是await()方法返回的原因
在System.out.println()
之前添加synchronized
块没有意义,此时所有线程都已完成
考虑使用AtomicInteger
作为number
而不是针对锁进行同步以调用+=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
只有当10个线程完成它们的工作时,doneSignal.await()才有效;行将被超越。这是不必要的,因为您正在等待“完成”信号。以一种方式刷新内存,使等待线程中的所有值对主线程可见
但是,您可以很容易地进行测试,在run
方法中进行一个需要几个(数百万)步骤的计算,并且不会被编译器优化,如果您看到的值与预期的最终值不同,那么您的最终值对于主线程来说是不可见的。当然,这里的关键部分是确保计算不会得到优化,因此一个简单的“增量”可能会得到优化。一般来说,当您不确定是否有正确的内存障碍时,这对测试并发性是有用的,因此以后可能会对您有用。需要时
没有
但是,由于没有(记录在案的)保证不会有任何交错,因此可以找到交错的日志条目
System.out.println("ABC");
System.out.println("123");
可以打印:
AB1
23C
值得
几乎可以肯定不是。大多数JVM将使用锁实现println
边缘大小写
正如@DimitarDimitrov所建议的,该锁还有一个可能的用途,那就是确保在访问数字之前跨越内存障碍。如果这是一个问题,那么您不需要锁定
,只需将编号
设置为易失性
private static volatile long number = 0L;
System.out.println(编号)周围不需要synchronized
,但这不是因为PrintWriter.println()
实现在内部同步,也不是因为在doneSignal.await()时取消阻止所有工作线程的工作
synchronized
是不需要的,因为在每次调用doneSignal.countDown
和完成doneSignal.await()
之前,都有一个before边。这保证了您将成功地看到number
的正确值。不管线程是否完成,重要的是读者是否可以看到实际的数值。幸运的是,pair countdown()+await()保证发生在:)之前。对于doneSignal.await()
来说,重要的是CountDownLatch
达到了0,而number
发生了什么根本不重要。如果他同时删除了CountDownLatch
,那么根据调度程序的不同,主线程可能会在所有Worker
线程完成之前完成,并打印0
或其他一些数字。我认为qwwdfsad的要点是,如果在每次倒计时()之前的代码之间没有发生
和await()
之后的代码,您仍然可以看到number
的值小于10。例如,如果await()
通过检查一个用旧的(Java 5.0之前的)volatile
语义更新的值来工作,它可以可靠地看到所有线程都已完成,但无法可靠地看到number
的最后一个值。谢谢@DimitarDimitrov,我现在得到了qwwdfsad所指的内容。然而,这些线程确实调用了倒计时()
,因此“完成”包括信号传递,这就是保证在@micheleb出现问题之前不会发生的事情——没有人认为之前不会发生任何事情。我(并且我假设@qwwdsad)所争论的是,在打印结果时,before edge是不需要同步的主要原因,而不是所有线程都已完成的事实。OP关心的是查看number
的正确更新值。通过同步的println()
实现无法解决此问题(除非对编号
的更新是在同一监视器中完成的,很可能是System.out
)。关于volatile
u
private static volatile long number = 0L;