2

From this question : AtomicInteger lazySet vs. set and form this link : https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html

I could gather following points

  • lazySet could be faster than set
  • lazySet uses store-store barrier (writes before are honored but not the contended writes, which were yet to happen)

I could find one use-case where it could be applied, from the documentation :

  • Use lazySet when you want to null out a pointer to aid GC.

Are there any other practical use-cases for lazySet ?

Community
  • 1
  • 1
Ajeet Ganga
  • 8,353
  • 10
  • 56
  • 79
  • One more example: http://psy-lob-saw.blogspot.ru/2013/03/single-producerconsumer-lock-free-queue.html (see "Lazy loving: lazySet replaces volatile write" section) – AnatolyG May 28 '15 at 17:14

3 Answers3

5

Caffeine uses lazy or relaxed writes in many of its data structures.

  • When nulling out a field (e.g. ConcurrentLinkedStack)
  • When writing to volatile fields before publishing (e.g. SingleConsumerQueue)
  • When publish is safely delayable (e.g. BoundedBuffer)
  • When races are benign (e.g. cache expiration timestamps)
  • When inside a lock (e.g. BoundedLocalCache)

ConcurrentLinkedQueue uses relaxed writes prior to publishing a node and may lazily sets a node's next field (prior to publishing or to indicate a stale traversal).

You may also enjoy reading the Linux Kernel Memory Barriers paper.

Ben Manes
  • 9,178
  • 3
  • 35
  • 39
3

TL;DR How to use .lazySet()? With care, if at all.

The main problem here is that AtomicXXX.lazySet() is low-level performance optimization and it is out of current JLS. You can't prove correctness if your concurrent code with JMM tools if you are using lazySet().

Why it is much faster than volatile write?

Main difference between set and lazySet is absence of StoreLoad barrier.

JSR-133 Cookbook for Compiler Writers:

StoreLoad barriers are needed on nearly all recent multiprocessors, and are usually the most expensive kind.

Moreover, on most popular x86-based hardware StoreLoad is the only explicit barrier (others are just no-op's and cost nothing), so with lazySet you eliminate all (explicit) memory barriers.

Guarantees of lazySet

From the point of JLS there isn't any. But actually you can reason about lazySet as delayed write which cannot be reordered with any previous write and will happen eventually. Eventually is finite time, if your process makes any progress (e.g. any synchronization action occurs; in addition, size of processor's store buffer is finite). If written value became visible for other thread, you can be sure that all previous writes are visible either (although you cannot formally prove it). So you can treat it as delayed happens-before relationship (but, of course, it's not even close to it's strict and formal definition).

Usage

Most practical usage (except nulling-out references) is making writes far cheaper in context of progress. Simplest example is using lazySet() instead of set() within synchronized block (but in this case there is no great performance impact). Or you can use it instead of writes in single producer cases, when no compete on write occurs. Disruptor developers are using lazySet exactly for this purpose in their lock-free implementation. Again, it's very hard to argue about correctness of such code, but it's good trick to be aware of.

qwwdfsad
  • 3,077
  • 17
  • 25
  • *From the point of JLS there isn't any.* Well, there are some guarantees for a StoreStore barrier, no? – John Vint May 26 '15 at 19:47
  • 2
    There is no such abstraction as barrier in JLS. Even reordering isn't a part of formal definitions. Only reasonings about visibilities and program order are presented in JLS 17. StoreStore barrier isn't part of specification, it's just a part of intetnal implementation – qwwdfsad May 26 '15 at 19:51
  • 1
    Fair enough, if you instead said JMM I guess that is different then. – John Vint May 26 '15 at 19:53
  • 1
    JMM is described in JLS, so it's debatable :) But yes, when saying JLS I mean JMM here. – qwwdfsad May 26 '15 at 19:55
2

I would think many uses of AtomicBoolean would benefit from the usage of lazySet() because they are often used as flags to indicate whether something is complete or not, or an outer loop should finish.

This is because in this case the value is initially one value and it eventually becomes another value and then stays there. Obviously this argument applied to almost any atomic that is used in that way.

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();
}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213