Is an AtomicReference required for visibility between threads?

I work with a framework that requires a callback when sending a request. Each callback must implement this interface. Methods in the callback are invoked asynchronously.

public interface ClientCallback<RESP extends Response> { public void onSuccessResponse(RESP resp); public void onFailureResponse(FailureResponse failure); public void onError(Throwable e); } 

To write integration tests with TestNG, I wanted to have a blocking callback. So I used CountDownLatch to synchronize between threads.

Is AtomicReference really necessary here or is it the correct link? . I know that if I use a raw link and a raw integer (instead of CountDownLatch), the code will not work because visibility is not guaranteed. But since CountDownLatch was already synchronized, I was not sure if I needed additional synchronization from AtomicReference. Note. The Result class is immutable.

 public class BlockingCallback<RESP extends Response> implements ClientCallback<RESP> { private final AtomicReference<Result<RESP>> _result = new AtomicReference<Result<RESP>>(); private final CountDownLatch _latch = new CountDownLatch(1); public void onSuccessResponse(RESP resp) { _result.set(new Result<RESP>(resp, null, null)); _latch.countDown(); } public void onFailureResponse(FailureResponse failure) { _result.set(new Result<RESP>(null, failure, null)); _latch.countDown(); } public void onError(Throwable e) { _result.set(new Result<RESP>(null, null, e)); _latch.countDown(); } public Result<RESP> getResult(final long timeout, final TimeUnit unit) throws InterruptedException, TimeoutException { if (!_latch.await(timeout, unit)) { throw new TimeoutException(); } return _result.get(); } 
+5
source share
4 answers

Here you do not need to use another synchronization object (AtomicRefetence). The fact is that the variable is set before the CountDownLatch is called in one thread and is read after the CountDownLatch is called in the other thread. CountDownLatch already performs thread synchronization and causes a memory barrier, so the order of writing before and reading after that is guaranteed. Because of this, you do not even need to use volatile for this field.

+2
source

A good starting point is javadoc (my attention):

Memory consistency effects: Until the counter reaches zero , actions in the thread before calling countDown() occur before actions after successfully returning from the corresponding await() in another thread.

Now there are two options:

  • either you never call set t onXxx methods as soon as the number is 0 (i.e. you only call one of the methods once) and you don’t need additional synchronization
  • or you can call the configuration methods more than once, and you will need additional synchronization.

If you are in scenario 2, you need to make the variable at least volatile (there is no need for AtomicReference in your example).

If you are in scenario 1, you need to decide how much defense you want:

  • To make a mistake on the safe side, you can still use volatile
  • if you are happy that the calling code does not spoil the class, you can use a regular variable, but I would at least explain in javadoc methods that only the first call to onXxx methods will be guaranteed to be visible.

Finally, in scenario 1, you can use the fact that installers can only be called once, in which case you probably use the AtomicReference method and its compareAndSet to make sure the link was null in advance and throw an exception otherwise.

+1
source

In order for the destination to be visible in streams, it is necessary to cross some kind of memory barrier. This can be done in several different ways, depending on what exactly you are trying to do.

  • You can use the volatile field. Reads and writes to volatile fields are atomic and visible in streams.
  • You can use AtomicReference . This is effectively the same as the volatile field , but it is a bit more flexible (you can reassign and pass links to AtomicReference ) and has some additional operations, such as compareAndSet() .
  • You can use the class CountDownLatch or similar
0
source

The short answer is you don't need AtomicReference here. However, you will need instability.

The reason is that you only write and read from the link (Result) and do not perform any compound operations such as compareAndSet ().

Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).

Reference, Sun Java Tutorial
https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html

Then there is JLS (Java language specification)

Writes and reads of links are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.

Java 8
http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7
Java 7
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7
Java 6
http://docs.oracle.com/javase/specs/jls/se6/html/memory.html#17.7

Source: https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html

Atomic actions cannot alternate, so they can be used without fear of flow interference. However, this does not eliminate the need for synchronization of atomic actions, since memory matching errors are still possible. The use of volatile variables reduces the risk of memory consistency errors, since any write to a mutable variable establishes a connection between events and subsequent readings of the same variable . This means that changes in a mutable variable are always visible to other threads. What else, it also means that when a stream reads a volatile variable, it sees not only the last change in volatiles, but also side effects of the code that led to the change.

Since you have only one write / read operation and it is atomic, which makes the volatile variable sufficient.

Regarding the use of CountDownLatch, he was waiting for the completion of n operations in other threads. Since you have only one operation, you can use Condition rather than CountDownLatch.

If you are interested in using AtomicReference, you can check out Java Concurrency in Practice (Page 326), find the following book:

https://github.com/HackathonHackers/programming-ebooks/tree/master/Java

Or the same example used by @Binita Bharti in the next StackOverflow answer
When to use AtomicReference in Java?

0
source

Source: https://habr.com/ru/post/1237149/


All Articles