通过以下问题:AtomicInteger lazySet vs. set并形成此链接:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html
我可以收集以下几点
我可以从文档中找到一个可以应用它的用例:
lazySet还有其他实际用例吗?
答案 0 :(得分:3)
Caffeine在其许多数据结构中使用延迟或放松写入。
ConcurrentLinkedQueue在发布节点之前使用宽松写入,并且可能懒惰地设置节点的下一个字段(在发布之前或指示过时的遍历)。
您可能还喜欢阅读Linux Kernel Memory Barriers论文。
答案 1 :(得分:2)
TL; DR 如何使用.lazySet()
?小心,如果有的话。
这里的主要问题是AtomicXXX.lazySet()
是低级性能优化,并且它不在当前的JLS中。如果使用lazySet()
,如果使用JMM工具并发代码,则无法证明其正确性。
为什么它比volatile写得快?
set
和lazySet
之间的主要区别是缺少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();
}