在Java中使用AtomicXXX上的lazySet

时间:2015-05-26 09:50:44

标签: java concurrency java.util.concurrent

通过以下问题:AtomicInteger lazySet vs. set并形成此链接:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html

我可以收集以下几点

  • lazySet可能比设置
  • 更快
  • lazySet使用商店存储障碍(之前的写入被尊重但不是竞争的写入,但尚未发生)

我可以从文档中找到一个可以应用它的用例:

  • 如果要将指针归零以帮助GC,请使用lazySet。

lazySet还有其他实际用例吗?

3 个答案:

答案 0 :(得分:3)

Caffeine在其许多数据结构中使用延迟或放松写入。

  • 归零字段时(例如ConcurrentLinkedStack)
  • 在发布之前写入易失性字段(例如SingleConsumerQueue)
  • 发布可安全延迟时(例如BoundedBuffer)
  • 当比赛是良性的(例如缓存到期时间戳)
  • 在锁内(例如BoundedLocalCache)

ConcurrentLinkedQueue在发布节点之前使用宽松写入,并且可能懒惰地设置节点的下一个字段(在发布之前或指示过时的遍历)。

您可能还喜欢阅读Linux Kernel Memory Barriers论文。

答案 1 :(得分:2)

TL; DR 如何使用.lazySet()?小心,如果有的话。

这里的主要问题是AtomicXXX.lazySet()是低级性能优化,并且它不在当前的JLS中。如果使用lazySet(),如果使用JMM工具并发代码,则无法证明其正确性。

为什么它比volatile写得快?

setlazySet之间的主要区别是缺少StoreLoad障碍。

JSR-133 Cookbook for Compiler Writers

  

几乎所有最近的多处理器都需要StoreLoad屏障,并且通常是最昂贵的类型。

此外,在大多数流行的基于x86的硬件上,StoreLoad是唯一明确的障碍(其他只是无操作且无需任何费用),因此使用lazySet可以消除所有(显式)内存障碍。

保证lazySet

从JLS的角度来看,没有任何内容。 但实际上你可以推断lazySet作为延迟写入,它不能与任何先前的写入重新排序,并最终会发生。 最终是有限时间,如果您的流程取得任何进展(例如,任何synchronization action发生;此外,处理器的存储缓冲区的大小是有限的)。如果其他线程可以看到写入值,则可以确保所有先前的写入都可见(尽管您无法正式证明它)。因此,您可以将其视为延迟发生 - 之前的关系(当然,它甚至不接近它的严格和正式的定义)。

<强>用法

大多数实际用法(除了归零引用除外)使得写入在进度的上下文中便宜得多。最简单的示例是在synchronized块中使用lazySet()而不是set()(但在这种情况下,不会对性能产生很大影响)。或者,当没有写入竞争时,您可以在单个生产者案例中使用它而不是写入。 Disruptor开发人员在无锁实现中正是为了这个目的而使用lazySet。同样,很难说这些代码的正确性,但这是一个很好的诀窍。

答案 2 :(得分:1)

我认为AtomicBoolean的许多用法都会受益于lazySet()的使用,因为它们通常用作标记来指示某些内容是否完整,或者外部循环应该完成。

这是因为在这种情况下,该值最初是一个值,它最终成为另一个值,然后保持在那里。显然,这个论点适用于几乎所有以这种方式使用的原子。

public void test() {
    final AtomicBoolean finished = new AtomicBoolean(false);
    new Thread(new Runnable() {

        @Override
        public void run() {
            while (!finished.get()) {
                // A long process.
                if (wereAllDone()) {
                    finished.lazySet(true);
                }
            }
        }

    }).start();
}