afs*_*tos 4 java multithreading synchronized happens-before
我对Java的发生和同步有一些分歧.
想象一下以下场景:
主线程
MyObject o = new MyObject(); // (0)
synchronized (sharedMonitor) {
// (1) add the object to a shared collection
}
// (2) spawn other threads
Run Code Online (Sandbox Code Playgroud)
其他主题
MyObject o;
synchronized (sharedMonitor) {
// (3) retrieve the previously added object
}
// (4) actions to modify the object
Run Code Online (Sandbox Code Playgroud)
请注意,实例变量MyObject既不是volatile,也不是final.MyObject不使用同步的方法.
我的理解是:
1 发生在 3 之前,因为在同一个监视器上有同步,而其他线程仅在2处生成,这在1之后执行.
4上的操作无法保证以后对主线程可见,除非所有线程都进一步同步,并且主线程在这些操作之后以某种方式同步.
问:是否可以保证0处的操作可见,之前发生,3上的并发访问,或者我必须将变量声明为volatile?
现在考虑以下场景:
主线程
MyObject o = new MyObject(); // (0)
synchronized (sharedMonitor) {
// (1) add the object to a shared collection
}
// (2) spawn other threads, and wait for their termination
// (5) access the data stored in my object.
Run Code Online (Sandbox Code Playgroud)
其他主题
MyObject o;
synchronized (sharedMonitor) {
// (3) retrieve the previously added object
}
o.lock(); // using ReentrantLock
try {
// (4) actions to modify the object
} finally { o.unlock(); }
Run Code Online (Sandbox Code Playgroud)
我的理解是:
1 发生在 3 之前,就像之前一样.
由于在持有的同步,其他线程之间的操作在4之间是可见ReentrantLock的MyObject.
4上的操作逻辑上发生在3之后,但是由于在不同的监视器上同步,因此在3到4之间没有发生关系.
上面的点仍将如此,即使是在同步sharedMonitor后unlock的4.
即使主线程等待其他任务终止,4 之前的操作也不会发生 -在5上的访问之前.这是由于5上的访问未与之同步o.lock(),因此主线程仍可能看到过时的数据.
问:我的理解是否正确?
问:是否可以保证0处的操作可见,3之前发生,并发访问,或者我必须将变量声明为volatile?
是的,有保证.你不是需要有synchronized在主线程块,因为有一个之前发生时,线程开始的关系.从JLS 17.4.5开始:"在一个线程上调用start()发生 - 在启动线程中的任何操作之前."
这也意味着如果你传入o线程构造函数,你也不需要synchronized块(3).
(4)逻辑上的动作发生在(3)之后,但是在(3)到(4)之间的关系之前没有发生,因为在不同的监视器上同步.
是的,不是.逻辑顺序意味着在同一个线程中肯定存在一个先发生过的关系,即使它是不同的监视器.即使编译器处理不同的监视器,编译器也无法重新排序4到4.访问volatile字段也是如此.
对于多个线程,因为(3)只读取对象,所以没有竞争条件.但是,如果(3)正在对对象进行修改(而不是仅仅读取它),那么在另一个线程中,这些修改可能在(4)处看不到.正如你引用和@StephenC重申的那样,JLS说只有在同一台监视器上保证之前发生的关系.JLS 17.4.5:"监视器上的解锁发生 - 在该监视器上的每次后续锁定之前."
即使在(4)解锁后sharedMonitor上存在同步,上述要点仍然适用.
往上看.
(4)上的操作不会发生 - 在访问(5)之前,即使主线程等待其他任务终止
thread.join()不会.一旦主线程调用并且它返回而没有被中断,那么主线程将与它所连接的线程的内存完全同步.在连接的线程和执行连接的线程之间存在一个发生在之前的关系.JLS 17.4.5:"线程中的所有动作都发生在任何其他线程从该线程上的join()成功返回之前."