在Java中模拟内存屏障以摆脱易失性读取

问题描述 投票:2回答:1

假设我有一个并发访问的字段,并且该字段被读取多次且很少写入。

public Object myRef = new Object();

假设线程T1将一分钟一次将myRef设置为另一个值,而其他N个线程将连续并发读取myRef数十亿次。我只需要myRef最终对所有线程可见。

一个简单的解决方案是使用AtomicReference或像这样简单的volatile:

public volatile Object myRef = new Object();

但是,afaik易失性读取确实会导致性能损失。我知道这是微不足道的,这更像是我想知道的东西,而不是我真正需要的东西。因此,让我们不必担心性能,并假设这是一个纯粹的理论问题。

因此问题归结为:是否有办法通过在写站点上执行某些操作来安全地绕过那些很少被写入的引用的易失性读取?

经过一番阅读之后,看来记忆障碍可能是我所需要的。因此,如果存在这样的构造,我的问题将得到解决:

  • 调用屏障(同步)
  • 所有内容都已同步并且所有线程都看到了新值(在读取站点上没有永久成本,在同步缓存时,它可能会花费一笔时间,但是在此之后,所有这些都回到常规字段,直到下一次写入为止。) >
  • Java中是否有这样的构造,或者一般而言?在这一点上,我不禁要想,如果存在类似的东西,那么将由维护这些程序包的更聪明的人/女孩已经将其合并到原子实现中。所以我的想法出了什么问题,根本不可能有这样的构造?

[我已经看到一些代码示例出于类似目的使用'volatile',利用了它发生在合同之前的情况。有一个单独的同步字段,例如:

public Object myRef = new Object();
public volatile int sync = 0;

以及在编写线程/站点时:

myRef = new Object();
sync += 1 //volatile write to emulate barrier

我不确定这是否可行,有人认为这仅适用于x86架构。在阅读了JMS中的相关章节之后,我认为只有在该易失性写入与需要查看myRef新值的线程中进行易失性读取相结合的情况下,才能保证工作正常。 (因此,不能摆脱易失性读取)。

回到我的原始问题;这有可能吗? Java可能吗? Java 9 VarHandles中的新API之一有可能吗?

假设我有一个同时访问的字段,并且该字段已被读取多次且很少写入。公共对象myRef = new Object();假设一个线程T1将myRef设置为另一个值,...

java memory concurrency volatile
1个回答
1
投票

易失性读取的性能成本与大多数现代JVM中的非易失性读取的性能成本一致。在后一种实现中,您仍然依赖于易失性写入,然后依靠易失性读取来获取对象引用的最新值,这与前者没有任何好处。请注意,如果在阅读myRef之前没有阅读sync变量,则不能保证您将看到该变量的最新值。如果您已经有一些易失性的读/写操作,则可以通过该操作来获得对内存的可见性。但是引入一个新的volatile变量来更新其他现有的引用根本没有任何意义。它具有相同的性能成本,并且不仅使您的代码更脆弱,更难以维护而且可读性更差。因此,在您的第一种方法中仅具有一个volatile变量是这里的最佳解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.