empty synchronized(this){}对线程之间的内存可见性有什么意义吗?

Pio*_*ler 27 java concurrency volatile synchronized memory-model

我在StackOverflow的评论中读到了这个:

但是如果你想要安全,你可以在@PostConstruct [方法]的末尾添加简单的synchronized(this){}

[注意变量不易变化]

我认为只有当写入和读取都是在块中执行或者至少读取是易失性时才会强制执行synchronized.

引用的句子是否正确?空synchronized(this) {}块是否将当前方法中更改的所有变量刷新为"一般可见"内存?

请考虑一些场景

  • 如果第二个线程从不调用锁定this怎么办?(假设第二个线程在其他方法中读取).请记住,问题是:刷新对其他线程的更改,而不是给其他线程一种方式(同步)来轮询原始线程所做的更改.在Spring @PostConstruct上下文中,很可能在其他方法中没有同步- 正如原始评论所说.

  • 是仅在另一个线程的第二次和后续调用中强制执行的更改的内存可见性?(请记住,这个同步块是我们方法中的最后一个调用) - 这会将这种同步方式标记为非常糟糕的做法(第一次调用中的陈旧值)

Bri*_*etz 15

关于这个问题的大部分内容,包括这个帖子中的许多答案/评论,遗憾地是错误的.

这里适用的Java内存模型中的关键规则是:在同一监视器上的后续锁定操作之前,对给定监视器执行解锁操作.如果只有一个线程获得锁定,则没有任何意义.如果VM可以证明锁定对象是线程限制的,则它可以忽略它可能以其他方式发出的任何栅栏.

您突出显示的引用假设释放锁定充当完整的栅栏.有时这可能是真的,但你不能指望它.所以你怀疑的问题是有根据的.

有关Java内存模型的更多信息,请参阅实践中的Java Concurrency,第16章.


Joh*_*int 6

a之前发生的所有写操作monitor exit都在a之后对所有线程可见monitor enter.

A synchronized(this){}可以变成字节码

monitorenter
monitorexit
Run Code Online (Sandbox Code Playgroud)

因此,如果你之前有一堆写,那么synchronized(this){}它们就会在之前发生monitorexit.

这将我们带到我的第一句话的下一个点.

a后可见所有线程 monitor enter

所以现在,为了让线程确保写入,它必须执行相同的同步,即synchornized(this){}.这将至少发布一个monitorenter并在订购之前确定您的发生.


所以回答你的问题

空的synchronized(this){}是否将当前方法中更改的所有变量刷新为"一般可见"内存?

是的,只要您想要读取那些非易失性变量时保持相同的同步.

解决您的其他问题

如果第二个线程永远不会锁定此怎么办?(假设第二个线程在其他方法中读取).请记住,问题是:刷新对其他线程的更改,而不是给其他线程一种方式(同步)来轮询原始线程所做的更改.在Spring @PostConstruct上下文中,很可能在其他方法中没有同步

那么在这种情况下使用synchronized(this)没有任何其他上下文是相对无用的.没有发生在之前的关系,它在理论上和不包括它一样有用.

是仅在另一个线程的第二次和后续调用中强制执行的更改的内存可见性?(请记住,这个同步块是我们方法中的最后一个调用) - 这会将这种同步方式标记为非常糟糕的做法(第一次调用中的陈旧值)

内存可见性由第一个线程调用强制synchronized(this),因为它将直接写入内存.现在,这并不一定意味着每个线程都需要直接从内存中读取.他们仍然可以从他们自己的处理器缓存中读取.进行线程调用synchronized(this)可确保从内存中提取字段的值并检索最新的值.