假设我有一个字段,被并发访问,读取次数很多且很少写入。
public Object myRef = new Object();
假设有一个线程T1每分钟将myRef设置为另一个值,而其他N个线程将连续且并发地读取myRef数十亿次。我只需要确保所有线程最终都能看到myRef的值。
一个简单的解决方案是使用AtomicReference或者像下面这样简单的volatile:
public volatile Object myRef = new Object();
然而,据我所知,volatile读操作确实会带来性能开销。我知道这种开销很小,这更像是我想知道的东西而不是我真正需要的东西。因此,让我们不要关注性能,假设这是一个纯理论问题。
因此,问题归结为:是否有一种安全地绕过仅偶尔写入的引用的volatile读取方式,通过在写入站点执行某些操作? 经过一些阅读,看起来内存屏障可能就是我需要的东西。因此,如果存在这样的结构体,我的问题将得到解决:
- 写入
- 调用屏障(同步)
- 一切都同步了,所有线程都将看到新值。(在读取站点没有永久成本的情况下,它可以是旧的,或者产生一次缓存同步的成本,但在那之后,一切都恢复到常规字段获取直到下一次写入)。
public Object myRef = new Object();
public volatile int sync = 0;
在编写线程/网站时:
myRef = new Object();
sync += 1 //volatile write to emulate barrier
我不确定这是否有效,有些人认为这仅适用于x86架构。在阅读JMS中的相关部分后,我认为只有当需要查看myRef新值的线程进行了volatile读操作与volatile写操作配对时,才能保证其有效。(因此不能消除volatile读取)。
回到我的最初问题:这是否可能?在Java中可能吗?在Java 9 VarHandles中的某个新API中可能吗?
sync += 1;
,并且您的读取线程读取sync
值,它们也会看到myRef
的更新。因为您只需要使读者最终看到更新,所以可以利用这一点,在读者线程的每1000次迭代(或类似情况)中仅读取同步。但您也可以使用volatile
进行类似的技巧 - 只需在读者中缓存myRef
字段1000次迭代,然后再次使用volatile读取... - Petr Janečeksync
字段,那么读者在每次迭代时不会触及sync
字段,他们会在想要检查是否有更新时机会主动这样做。话虽如此,一个更简单的解决方案是将myRef
缓存1000轮,然后重新读取它... - Petr Janeček