empty synchronized(this){}对线程之间的内存可见性有什么意义吗?
我在StackOverflow的评论中读到了这个:
但是如果你想要安全,你可以在@PostConstruct [方法]的末尾添加简单的synchronized(this){}
[注意变量不易变化]
我认为只有在synchronized
块中执行写入和读取或者至少读取是易失性时才强制执行before-before 。
引用的句子是否正确? 空的synchronized(this) {}
块是否将当前方法中更改的所有变量刷新为“一般可见”内存?
请考虑一些场景
-
如果第二个线程永远不会锁定
this
怎么办? (假设第二个线程读入其他方法)。 请记住,问题是: 刷新对其他线程的更改 ,而不是给其他线程一种方式(同步)来轮询原始线程所做的更改 。 在Spring@PostConstruct
上下文中,很可能在其他方法中没有同步 – 正如原始评论所说。 -
是仅在另一个线程的第二次和后续调用中强制执行的更改的内存可见性? (请记住,这个同步块是我们方法中的最后一次调用) – 这会将这种同步方式标记为非常糟糕的做法(第一次调用中的陈旧值)
monitor exit
之前发生的所有写入在monitor enter
后对所有线程都可见。
synchronized(this){}
可以变成字节码
monitorenter monitorexit
因此,如果您在synchronized(this){}
之前有一堆写入,那么它们将在monitorexit
之前发生。
这将我们带到我的第一句话的下一个点。
monitor enter
后,所有线程都可见
所以现在,为了让线程确保写入,它必须执行相同的同步,即synchornized(this){}
。 这将至少发出一个monitorenter
并在订购之前确定你的发生。
所以回答你的问题
空的synchronized(this){}块是否将当前方法中更改的所有变量刷新为“一般可见”内存?
是的,只要您想要读取那些非易失性变量时保持相同的同步。
解决您的其他问题
如果第二个线程永远不会锁定此怎么办? (假设第二个线程读入其他方法)。 请记住,问题是:刷新对其他线程的更改,而不是给其他线程一种方式(同步)来轮询原始线程所做的更改。 在Spring @PostConstruct上下文中,很可能在其他方法中没有同步
那么在这种情况下使用synchronized(this)
而没有任何其他上下文是相对无用的。 没有发生在之前的关系,它在理论上和不包括它一样有用。
是仅在另一个线程的第二次和后续调用中强制执行的更改的内存可见性? (请记住,这个同步块是我们方法中的最后一次调用) – 这会将这种同步方式标记为非常糟糕的做法(第一次调用中的陈旧值)
内存可见性由第一个调用synchronized(this)
线程强制执行,因为它将直接写入内存。 现在,这并不一定意味着每个线程都需要直接从内存中读取。 他们仍然可以从他们自己的处理器缓存中读取。 线程调用synchronized(this)
可确保从内存中提取字段的值并检索最新的值。
关于这个问题的大部分内容,包括这个post中的许多答案/评论,遗憾的是错误的。
这里适用的Java内存模型中的关键规则是:在同一监视器上的后续锁定操作之前,对给定监视器执行解锁操作。 如果只有一个线程获得锁定,则没有任何意义。 如果VM可以certificate锁定对象是线程限制的,则它可以忽略它可能以其他方式发出的任何栅栏。
您突出显示的引用假定释放锁定充当完整的栅栏。 有时这可能是真的,但你不能指望它。 所以你怀疑的问题是有根据的。
有关Java内存模型的更多信息,请参阅实践中的Java Concurrency ,第16章。