提问者:小点点

空同步(this){}对线程之间的内存可见性有任何意义吗?


我在StackOverflow上的一篇评论中看到了这一点:

但是如果想安全起见,可以在you @PostConstruct [method]的末尾加上简单的synchronized(this) {}

[请注意,变量不是易失性的]

我在想,只有当写和读都在<code>synchronized</code>块中执行,或者至少读是易失的时,才会强制执行之前的操作。

引用的句子正确吗?空的同步(this){}块是否将当前方法中更改的所有变量刷新到“通用可见”内存?

请考虑一些场景

>

  • 如果第二个线程从不调用 Lock 怎么办?(假设第二个线程在其他方法中读取)。请记住,这个问题是关于:将更改刷新到其他线程,而不是为其他线程提供轮询原始线程所做的更改的方法(同步)。在 Spring @PostConstruct上下文中,其他方法中的不同步也很可能 - 正如原始评论所说。

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


  • 共2个答案

    匿名用户

    很遗憾,在SO上写的很多东西,包括这个帖子里的很多回答/评论,都是错误的。

    此处适用的 Java 内存模型中的关键规则是:在给定监视器上执行解锁操作之前,然后在同一监视器上执行后续锁定操作。如果只有一个线程获取了锁,则它没有任何意义。如果 VM 可以证明锁定对象是线程受限的,则可以避开它可能发出的任何Geofence。

    你强调的引用假设释放锁就像一个完整的栅栏。有时这可能是真的,但你不能指望它。所以你的怀疑问题是有根据的。

    有关Java内存模型的更多信息,请参阅Java并发实践,第16章。

    匿名用户

    监视器退出之前发生的所有写入对监视进入之后的所有线程都可见。

    一个同步的(this){}可以变成字节码,如

    monitorenter
    monitorexit
    

    因此,如果在同步(this){}之前有一堆写操作,它们将在monitorexit前发生。

    这就引出了我第一句话的下一点。

    监视器输入后,所有线程都可见

    现在,为了让线程确保写操作发生,它必须执行相同的同步,即< code > synchronized(this){ } 。这将至少发出一个< code>monitorenter,并在订购前确定您的发生情况。

    所以回答你的问题

    空的同步(this){}块是否将当前方法中更改的所有变量刷新为“通用可见”内存?

    是的,只要您在读取这些非易失性变量时保持相同的同步即可。

    来回答你的其他问题

    如果第二个线程从不对此调用lock怎么办?(假设第二个线程在其他方法中读取)。记住这个问题是关于:刷新对其他线程的更改,不要给其他线程一种方法(同步)来轮询原始线程所做的更改。在Spring@PostCon结构体上下文中,其他方法中的不同步也很可能

    在这种情况下,在没有任何其他上下文的情况下使用< code>synchronized(this)是相对无用的。没有发生之前的关系,从理论上来说,它和不包含它一样有用。

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

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