【问题标题】:Why volatile read always uncontended?为什么 volatile 读取总是无竞争的?
【发布时间】:2017-03-06 13:12:25
【问题描述】:

引自书籍java并发实践:

同步的性能成本来自多个来源。 synchronized 和 volatile 提供的可见性保证可能 需要使用称为内存屏障的特殊指令 刷新或使缓存无效、刷新硬件写入缓冲区和停止 执行管道。记忆障碍也可能有间接的 性能后果,因为它们抑制了其他编译​​器 优化;大多数操作不能用内存重新排序 障碍。在评估同步的性能影响时,它 区分有争议的和无争议的很重要 同步。同步机制针对 无竞争的情况(volatile 总是无竞争的),此时 写作,“快速Ͳpath”的性能成本无可争辩 大多数系统的同步范围为 20 到 250 个时钟周期。

您能否更清楚地说明这一点? 如果我有大量线程读取 volaile 变量怎么办?

您能否提供争用定义?

是否有衡量争用的工具?它是在哪些价值观中衡量的?

【问题讨论】:

    标签: java concurrency locking volatile contention


    【解决方案1】:

    您能否更清楚地说明这一点?

    这是一个涉及很多主题的密集段落。您具体要求澄清哪个或哪些主题?你的问题太宽泛了,无法令人满意地回答。对不起。

    现在,如果您的问题是特定于 uncontended 同步,这意味着 JVM 中的线程不必阻塞、解除阻塞/通知然后回到阻塞状态。

    在底层,JVM 使用硬件特定的内存屏障来确保

    1. volatile 字段始终从主内存读取和写入,而不是从 CPU/核心缓存读取和写入,并且
    2. 您的线程不会阻止/解除阻止来访问它。

    没有争议。当您使用同步块 OTH 时,您的所有线程都处于阻塞状态,除了一个线程读取同步块保护的任何数据。

    让我们调用那个线程,即访问同步数据的线程,线程 A

    现在,这是关键,当 线程 A 处理完数据并存在同步块时,这会导致 JVM 唤醒所有其他线程正在/正在等待 线程 A 退出同步块。

    它们都被唤醒(这在 CPU/内存方面很昂贵)。他们都在争先恐后地试图获得同步块。

    想象一下一大群人试图通过一个小房间离开拥挤的房间。是的,就像那样,这就是线程在尝试获取同步锁时的行为方式。

    但只有一个人得到它并进入。所有其他人都回到睡眠状态,有点,在所谓的阻塞状态。这在资源方面也很昂贵。

    因此,每当其中一个线程存在同步块时,所有其他线程都会发疯(我能想到的最佳心理形象)来访问它,一个得到它,然后所有其他线程都回到阻塞状态.

    这就是使同步块变得昂贵的原因。现在,请注意:在 JDK 1.4 之前,它曾经非常昂贵。那是17年前的事了。 Java 1.4 开始出现一些改进(2003 IIRC)。

    然后 Java 1.5 在 12 年前的 2005 年引入了更大的改进,这使得同步块的成本更低。

    记住这些事情很重要。那里有很多过时的信息。

    如果我有大量线程读取 volaile 变量怎么办?

    就正确性而言,这并不重要。无论线程数如何,volatile 字段将始终显示一致的值。

    现在,如果您有大量线程,性能可能会因为上下文切换、内存利用率等而受到影响(不一定和/或主要是因为访问 volatile 字段。

    您能否提供争用定义?

    请不要误会,但如果你问这个问题,恐怕你还没有完全准备好使用你正在阅读的书。

    您将需要更基本的并发介绍,特别是争用。

    https://en.wikipedia.org/wiki/Resource_contention

    最好的问候。

    【讨论】:

    • 好回答老兄。您谈论synchronized 块,但对锁定开销还不够。阻塞讨论暗示了这一点,但最好提及甚至持有然后释放同步锁的性能影响。此外,当一个线程退出同步块时,只有 1 个线程被解除阻塞,而不是全部。
    • 另外,“所有其他线程都发疯了(我能想到的最佳心理形象)才能访问它”是错误的。当线程解锁锁时,队列中等待锁的第一个线程(如果有)被赋予锁。所有线程都不会醒来并与之抗争。
    • @Gray 当你说错的时候,你是从synchronizedjava.util.concurrent 类的角度说话。 java.util.concurrent 类使用 CLH 锁定,这是一种基于队列的锁定,但我不确定和怀疑 synchronized 中的队列。如果synchronized 使用了队列,为什么不公平,为什么会导致饥饿?
    • 谁说@SaptarshiBasu 不公平?当然,这取决于操作系统的实现,但我不知道会导致饥饿。它通常是 FIFO。