gpt4 book ai didi

java - Java中的内存防护有什么用?

转载 作者:行者123 更新时间:2023-12-01 07:30:27 25 4
gpt4 key购买 nike

在尝试了解SubmissionPublisher(source code in Java SE 10, OpenJDK | docs)是如何实现的,该类是在版本9中添加到Java SE的新类,但我偶然发现了对odt_a的一些API调用,而我以前没有意识到:
VarHandlefullFenceacquireFencereleaseFenceloadLoadFence

在进行了一些研究之后,尤其是关于内存屏障/栅栏的概念(我以前曾听说过,是的;但是从未使用过它们,因此对它们的语义非常陌生),我认为我对它们的用途有基本的了解。 。但是,由于我的问题可能是由错误的观念引起的,因此我想确保我一开始就正确:

  • 内存障碍是与读写操作有关的重新排序约束。
  • 内存屏障可以分为两大类:单向和双向内存屏障,取决于它们是对读取还是写入或对这两者设置约束。
  • storeStoreFence ,但是,这些与VarHandle提供的不匹配。 但是,VarHandle中可用的某些内存屏障提供与其相应的C++内存屏障兼容的排序效果。
  • #fullFenceatomic_thread_fence(memory_order_seq_cst)兼容
  • #acquireFenceatomic_thread_fence(memory_order_acquire)兼容
  • #releaseFenceatomic_thread_fence(memory_order_release)兼容
  • #loadLoadFence#storeStoreFence没有兼容的C++计数器部分

  • “兼容”一词在这里似乎非常重要,因为在细节上语义明显不同。例如,所有C++障碍都是双向的,而Java障碍不是必需的。
  • C++ supports a variety of memory barriers这些特别取决于其他线程中使用的屏障类型和先前执行的屏障指令。由于障碍指令的全部含义是特定于硬件的,因此我将坚持使用更高级别的(C++)障碍。例如,在C++中,执行释放屏障指令的线程可以看到在释放屏障指令之前进行的更改。

  • 我的假设正确吗?如果是这样,我的问题是:
  • VarHandle中可用的内存屏障是否会导致某种形式的内存同步?
  • 不管它们是否引起内存同步,重新排序约束在Java中可能有用吗?当涉及 volatile 字段,锁或VarHandle之类的#compareAndSet操作时,Java内存模型已经为排序提供了非常有力的保证。

  • 如果您正在寻找一个示例:上面的 BufferedSubscription(上面链接的源代码)的内部类 SubmissionPublisher在1079行(函数 growAndAdd)中建立了完整的围栏,因为链接的网站不支持片段标识符,只需CTRL + F)。但是,我不清楚它的用途。

    最佳答案

    实际上,这主要是一个无答案的内容(最初希望将其添加为评论,但您可以看到,它太长了)。只是我自己问了很多问题,做了很多阅读和研究,因此我可以放心地说:这很复杂。我什至用jcstress编写了多个测试,以弄清它们是如何工作的(在查看生成的汇编代码的同时),尽管其中一些以某种方式有意义,但总的来说,这个主题绝非易事。

    您需要了解的第一件事:

    The Java Language Specification (JLS) does not mention barriers, anywhere. This, for java, would be an implementation detail: it really acts in terms of happens before semantics. To be able to proper specify these according to the JMM (Java Memory Model), the JMM would have to change quite a lot.



    这项工作正在进行中。

    其次,如果您真的想在这里爬取表面,请 this is the very first thing to watch。谈话真是不可思议。我最喜欢的部分是Herb Sutter举起5根手指说:“这是多少人可以真正,正确地使用它们。”那应该给您提示所涉及的复杂性。尽管如此,还是有一些容易掌握的琐碎示例(例如,由多个线程更新的计数器并不关心其他内存保证,而仅关心其本身是否正确递增)。

    另一个示例是(在Java中)当您想要 volatile标志来控制线程停止/启动时。您知道,经典的:
    volatile boolean stop = false; // on thread writes, one thread reads this    

    如果使用Java,您会知道没有 volatile就会破坏该代码(例如,您可以了解为什么没有它就会破坏双重检查锁定的原因)。但是您是否也知道,对于某些编写高性能代码的人来说,这太多了吗? volatile的读/写也可以保证 sequential consistency-具有一些有力的保证,并且某些人希望此版本更弱。

    A thread safe flag, but not volatile? Yes, exactly: VarHandle::set/getOpaque.



    您会质疑为什么有人可能需要这种东西?并不是每个人都对 volatile附带的所有更改感兴趣。

    让我们看看如何在Java中实现这一目标。首先,此类奇特的东西已经存在于API中: AtomicInteger::lazySet。这在Java内存模型和 has no clear definition中未指定;仍然有人使用它(LMAX,afaik或 this for more reading)。恕我直言, AtomicInteger::lazySetVarHandle::releaseFence(或 VarHandle::storeStoreFence)。

    让我们尝试回答 为什么有人需要这些吗?

    JMM基本上有两种访问字段的方式:普通和易失(保证顺序一致性)。您提到的所有这些方法都可以在这两者之间带来某些东西-发布/获取语义;我猜有些情况下人们确实需要这样做。

    从发布/获取到更大的放松将是不透明的,即 I am still trying to fully understand

    因此,底线(您的理解是相当正确的,顺便说一句):如果您打算在Java中使用它-目前他们还没有规范,则后果自负。如果您确实想了解它们,则可以从它们的C++等效模式开始。

    关于java - Java中的内存防护有什么用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60119169/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com