【问题标题】:What should I use as a memory barrier for a Collection?我应该使用什么作为集合的内存屏障?
【发布时间】:2016-05-05 17:07:09
【问题描述】:

假设我有一个 Java ArrayList,它显然不能是一个 volatile 变量(volatile 的意义是:它的所有内部变量都是 volatile),并且想在一个第二个线程,因为我可以确定第二个线程是在第一个线程结束之后执行的(第一个线程可能已经修改了ArrayList 实例)。

使用Memory Barrier 这应该很容易。但是我怎样才能构造这样一个影响ArrayList 实例的所有内部成员变量/状态的内存屏障呢?我知道同步是一种选择,但我不知道我应该在哪个对象上同步才能达到预期的效果。

有没有关于这个问题的官方参考,定义了最佳实践?

在我看来,实现内存屏障的最直接方法是调用 fullFence()。但似乎这不是推荐的方式;-)

【问题讨论】:

    标签: java multithreading concurrency memory-barriers memory-fences


    【解决方案1】:

    同步只要在读写时同步在同一个对象上就可以了,保证是一致的。我建议学习 Java Concurrency In Practic 以深入了解并发性。

    在您的情况下,线程的结尾提供了一个内存屏障,因此读取可能已被第一个线程修改的任何值应该是安全的。见https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html

    The final action in a thread T1 synchronizes-with any action in another thread T2 that detects that T1 has terminated.
    
    T2 may accomplish this by calling T1.isAlive() or T1.join().
    

    【讨论】:

    • 假设我的代码中有 Scala Future {...}.onComplete {...} 回调(onComplete 回调在 Future 的线程结束时调用,而在 onComplete 回调中,第二个线程由第二个Future:回调是真的第一个Future的线程结束后调用,还是结束前调用?
    【解决方案2】:

    我可以确定第二个线程是在第一个线程结束后执行的

    让我们调用你的两个线程 A 和 B,其中线程 B 是产生结果的线程,线程 A 是想要使用结果的运行时间较长的线程。

    你的A线程只需要调用B.join()。在 join() 调用返回后,线程 B 在死前写入内存的所有内容都保证对线程 A 可见。


    另一种方法是使用锁来保护有问题的数据。如果线程B在持有锁的同时写入数据,那么在B释放锁并且A获取它之后,线程A将保证看到数据。


    第三种方法是使用volatile 变量。您说有问题的数据不能声明为volatile,但访问 volatile 变量不仅仅影响变量本身。如果线程 B 更新数据,然后更新一些 volatile int i;,那么 B 在更新 i 之前写入内存的所有内容都将在 A 读取 i 后对 A 可见。

    【讨论】:

      【解决方案3】:

      您真正需要的是在列表中发生之前。状态更新结束时的全栅栏并不能保证读者会在列表中看到一致的、完全更新的值,例如因为一些读取会停留在核心invalidation queue 中,或者因为 JIT 会将一些值缓存在寄存器中,因为 VM 不知道您正在处理共享数据,因此代码的行为可能与您期望的方式不同。如果你不害怕 JIT(也许是因为你知道你正在以某种方式从内存中重新读取列表),那么只有在 x86 架构上,full-fence 就足够了,因为 x86 具有 TSO(总存储顺序)属性,所以障碍 ( ~fences)需要读取共享数据(LoadLoad 和 LoadStore)是actually no-ops。但是栅栏和障碍不是 JMM 或任何公共 API 的一部分,因此我们无法使用它们(Unsafe 除外)。只要 JMM 只为 well-formed execution 提供正确性保证(使用happens-before 和synchronized-with 关系),您就应该依赖这种机制,而不是棘手的Unsafe。

      要提供不同步的发生之前的关系,您可以添加额外的 volatile 变量并在更新结束时写入它,并在从列表读取之前读取它(写入 volatile 变量发生在从该变量读取写入值之前)。或者您可以在列表实例上进行简单的同步,具有相同的效果(释放监视器发生在获取相同的监视器之前)+ 互斥。

      【讨论】:

        猜你喜欢
        • 2011-03-30
        • 1970-01-01
        • 2015-05-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-29
        相关资源
        最近更新 更多