首页 文章

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

提问于
浏览
27

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

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

[注意变量不易变化]

我想只有在 synchronized 块中执行write和read或者至少read是volatile时才强制 happens-before .

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

Please consider some scenerios

  • 如果第二个线程永远不会在 this 上调用锁定怎么办? (假设第二个线程在其他方法中读取) . 请记住,问题是: flush changes to other threads ,而不是 give other threads a way (synchronized) to poll changes made by original thread . 其他方法中的非同步很可能在Spring @PostConstruct 上下文中 - 正如原始评论所说 .

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

2 回答

  • 6

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

    synchronized(this){} 可以变成像字节码一样

    monitorenter
    monitorexit
    

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

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

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

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


    所以回答你的问题

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

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

    解决您的其他问题

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

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

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

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

  • 13

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

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

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

    有关Java内存模型的更多信息,请参见Java Concurrency in Practice,第16章 .

相关问题