【问题标题】:Java multithreading purpose of synchronized keywordsynchronized关键字的Java多线程用途
【发布时间】:2021-05-16 21:04:14
【问题描述】:

Nutshell 6th edition 中的Java 一书可以阅读:

我们使用同步这个词作为“需要临时”的关键字的原因 独占访问”是JVM除了获取monitor之外,还重读 进入块时来自主存储器的对象的当前状态。相似地, 退出同步块或方法时,JVM 会刷新所有已修改的 对象的状态回主存。

还有:

如果没有同步,系统中不同的 CPU 核心可能看到的不一样 内存视图和内存不一致会损坏运行状态 程序,正如我们在 ATM 示例中看到的那样。

它建议当进入同步方法时,从主内存中加载对象以保持内存一致性

但是对于没有同步关键字的对象也是这样吗?那么如果一个 normal 对象在 CPU 的一个内核中被修改,那么它会与 main 内存同步,以便其他内核可以看到它?

【问题讨论】:

  • 嗨,你会考虑投票并接受答案吗,谢谢

标签: java multithreading concurrency synchronization thread-safety


【解决方案1】:

当另一个答案谈到缓存同步和内存层次结构的重要性时,synchronized 关键字并不能控制整个对象的状态;相反,它是关于与该对象关联的

Java 中的每个对象实例都可以有一个关联的锁,这可以防止多个线程同时运行那些在锁上同步的块。这要么隐含在 this 实例上,要么隐含在 synchronized 关键字的参数上(如果是静态方法,则是类)。

虽然 JMM 语义表明该锁定对象受到适当控制并且在缓存级别中可用,但这并不一定意味着该对象作为一个整体受到保护;例如,当单个线程在同步块或方法中运行时从不同线程读取的字段不会被处理。

此外,Java 内存模型定义了“发生在之前”的关系,即数据更改如何在您需要考虑的线程之间变得可见,这就是存在“volatile”关键字和 AtomicXxxx 类型(包括 var 句柄)的原因放松的记忆模型。

因此,当您谈论同步时,您需要注意它只拍摄对象锁定的状态,而不是它正在保护的对象内的状态。

【讨论】:

    【解决方案2】:

    首先,类似于其他未命中信息的情况,例如:

    Volatile 应该让线程从 RAM 中读取值 禁用线程缓存

    更多关于为什么不是这样的细节可以在this SO thread找到。

    这可以应用于语句:

    JVM 还会从主目录重新读取对象的当前状态 进块时的内存

    当同步块或方法退出时,JVM 会刷新任何 对象的修改状态回主存

    引用 David Schwarz 在 cmets 中指出以下内容并允许我使用:

    这在任何现代系统上都不会发生。这些是平台在理论上可能必须做的事情才能使同步工作,但如果它们在平台上不是必需的(并且它们不在您可能使用的任何平台上),它们还没有完成。 这些陈述都是关于没有硬件同步并且可能需要这些步骤的假设系统。 实际系统的硬件设计与这个简化的假设系统非常不同,需要完成的事情也大不相同。(例如,它们通常只需要优化和内存屏障,而不是刷新到主内存或读取。这是因为现代系统经过优化并使用缓存来避免必须刷新到主内存或从主内存重新读取,因为主内存非常慢,因此现代 CPU 有硬件优化来避免它。)


    现在回到你的问题:

    但是对于没有同步关键字的对象也是如此吗?所以在 CPU的一个核心中修改普通对象的情况是同步的 用主存让其他核心可以看到吗?

    TL;DR:它可能会发生,也可能不会发生;它取决于硬件以及是否从缓存中读取对象;但是,通过使用synchronized,JVM 可以确保它会。


    更详细的答案

    所以如果是普通对象在CPU的一个核心被修改是 与主存同步,以便其他内核可以看到?

    为了简单明了,没有同步它取决于将执行代码的硬件架构(例如缓存协议),并且取决于对象是否是(或不)在缓存中读取/更新。

    如果架构强制核心中的数据始终与其他核心一致,那么可以。访问缓存比访问主存快得多,访问第一级缓存(例如,L1)也比访问其他级别快。

    因此,出于性能原因,通常当从主内存加载数据(例如,对象)时,它会存储在缓存中(例如, L1、L2 , 和 L3) 以便在再次需要相同数据时更快地访问。

    第一级缓存往往是每个内核私有的。因此,可能会发生不同的内核在其私有缓存(例如,L1)中存储了“相同对象”的不同状态。因此,线程也可能正在读取/更新“相同对象”的不同状态。

    请注意,我写了 "same Object",因为从概念上讲它是同一个对象,但实际上它不是同一个实体,而是从 读取的同一个对象的副本主内存.

    【讨论】: