Does the value of AtomicReference will be set lazily if we assign a function to it which returns some array?

685 views Asked by At

I have this piece of code:

AtomicReference<List<String>> atomicStrings = new AtomicReference<>();
atomicStrings.set(someFunc());
Thread.sleep(10000);
System.out.print(String.join(",", atomicStrings.get()); // will this print a,b,c ?

Where

private List<String> someFunc() {
    List<String> list = new ArrayList<>();

    new Thread(() -> {
      try {
        list.add("a");
        Thread.sleep(1000);
        list.add("b");
        Thread.sleep(1000);
        list.add("c");
        Thread.sleep(1000);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }).start();

    return list;
}

Of course this is a very poor example but i tried to mimic my real test case here by adding delays. My question here is that since someFunc() returns the array right away and the array elements are populated in a different thread, but the result we get is stored in an AtomicReference and we don't get the value until later, the delay i am adding in the main function is more than the delay the new thread spawned will take. Will my returned array be populated will all the elements?

2

There are 2 answers

3
chrylis -cautiouslyoptimistic- On BEST ANSWER

You aren't "assigning a function" to it, you're immediately evaluating someFunc and placing the value (a reference to the list) in the AtomicReference.

The atomic classes have special happens-before constraints, so anything that happens to the list in someFunc is guaranteed to be visible to anyone who retrieves the list from the reference, but your modifications to the list in your spawned thread have no happens-before relationship to the rest of your program. The behavior is undefined, up to and including ConcurrentModificationException.

0
Gray On

Does the value of AtomicReference will be set lazily if we assign a function to it which returns some array?

First off, AtomicReference.set() is immediate and in no ways lazy. If we look at your code, we see that someFunc() returns an ArrayList so this will be set immediately into atomicStrings. Unfortunately, the strings are added to the list by another thread and there is no synchronization between the main thread, that is running someFunc() and creating the list, and the thread that is adding the strings to the list. Anytime two different threads are accessing the same object, and especially mutating that object, you need to worry about mutex (race conditions) and memory synchronization.

One thing you could use to fix your specific issue is to use a BlockingQueue which is a synchronized class instead of an ArrayList. BlockingQueue take care of all of the memory synchronization and mutex locking to ensure that accesses from multiple threads are done appropriately.

BlockingQueue<String> queue = new ArrayBlockingQueue<>();

Then, when the inner thread calls queue.add("a"); and then after the 10 seconds expire the main thread calls queue.iterator(), they will see the same collection of strings. There is no need for the AtomicReference because the atomic class the main and the inner threads will be sharing is the ArrayBlockingQueue .