开发者

AtomicXXX.lazySet(...) in terms of happens before edges

开发者 https://www.devze.com 2023-04-07 09:59 出处:网络
What does mean AtomicXXX.lazySet(value) method in terms of happens-before edges, used in most of JMM reasoning? The javadocs is pure on it, and Sun bug 6275329 states:

What does mean AtomicXXX.lazySet(value) method in terms of happens-before edges, used in most of JMM reasoning? The javadocs is pure on it, and Sun bug 6275329 states:

The semantics are that the write is guaranteed not to be re-ordered with any previous write, but may be reordered with subsequent operations (or equivalently, might not be visible to other threads) until some other volatile write or synchronizing action occurs).

But this not a reasoning about HB edges, so it confuses me. Does it mean what lazySet() semantics can't be expressed in terms of HB edges?

UPDATE: I'll try to concretize my question. I can use ordinary volatile field in following scenario:

//thread 1: producer
...fill some data structure
myVolatileFlag = 1;

//thread 2: consumer
while(myVolatileFlag!=1){
   //spin-wait
}
...use data structure...

In this scenario use of "data structure" in consumer is correct, since volatile flag write-read make HB edge, giving guarantee what all writes to "data structure" by producer will be completed, and visible by consumer. But what if I'll use AtomicInteger.lazySet/get instead of volatile write/read in this scenario?

//thread 1: producer
...fill some data structure
myAtomicFlag.lazySet(1);

//thread 2: consumer
while(myAtomicFlag.get()!=1){
   //spin-wait
}
...use data structure...

will it be still correct? Can I still really on "data structure" values visibility in consumer thread?

It is not "from air" question -- I've seen such method in LMAX Disruptor code in exactly this scenario, and I don't understand how to prove 开发者_如何学Pythonit is correct...


The lazySet operations do not create happens-before edges and are therefore not guaranteed to be immediately visible. This is a low-level optimization that has only a few use-cases, which are mostly in concurrent data structures.

The garbage collection example of nulling out linked list pointers has no user-visible side effects. The nulling is preferred so that if nodes in the list are in different generations, it doesn't force a more expensive collection to be performed to discard the link chain. The use of lazySet maintains hygenic semantics without incurring volatile write overhead.

Another example is the usage of volatile fields guarded by a lock, such as in ConcurrentHashMap. The fields are volatile to allow lock-free reads, but writes must be performed under a lock to ensure strict consistency. As the lock guarantees the happens-before edge on release, an optimization is to use lazySet when writing to the fields and flushing all of the updates when unlocking. This helps keep the critical section short by avoiding unnecessary stalls and bus traffic.

If you write a concurrent data structure then lazySet is a good trick to be aware of. Its a low-level optimization so its only worth considering when performance tuning.


Based on the Javadoc of Unsafe (the putOrderedInt is used in the AtomicInteger.lazySet)

/** 
 * Version of {@link #putObjectVolatile(Object, long, Object)}
 * that does not guarantee immediate visibility of the store to
 * other threads. This method is generally only useful if the
 * underlying field is a Java volatile (or if an array cell, one
 * that is otherwise only accessed using volatile accesses).
 */
public native void  putOrderedObject(Object o, long offset, Object x);

/** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)}  */
public native void  putOrderedInt(Object o, long offset, int x);

The backing fields in the AtomicXXX classes are volatile. The lazySet seems to write to these fields as if they are not volatile, which would remove the happens-before edges you are expecting. As noted in your link this would be useful for nulling values to be eligible for GC without having to incur the volatile write.

Edit:

This is to answer your update.

If you take a look at the quote you provided from the link then you lose any memory guarantees you had with the volatile write.

The lazySet will not be ordered above where it is being written to, but without any other actual synchronization you lose the guarantee that the consumer will see any changes that were written before it. It is perfectly legal to delay the write of the myAtomicFlag and there for any writes before it until some other form of synchronization occurs.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号