433

When do we use AtomicReference?

Is it needed to create objects in all multithreaded programs?

Provide a simple example where AtomicReference should be used.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Chintu
  • 4,333
  • 3
  • 15
  • 4

7 Answers7

282

Atomic reference should be used in a setting where you need to do simple atomic (i.e. thread-safe, non-trivial) operations on a reference, for which monitor-based synchronization is not appropriate. Suppose you want to set a specific field only if the state of the object has changed during processing:

AtomicReference<Object> cache = new AtomicReference<Object>();

Object cachedValue = new Object();
cache.set(cachedValue);

//... time passes ...
Object cachedValueToUpdate = cache.get();
//... do some work to transform cachedValueToUpdate into a new version
Object newValue = someFunctionOfOld(cachedValueToUpdate);
boolean success = cache.compareAndSet(cachedValue,newValue);

Because of the atomic reference semantics, you can do this even if the cache object is shared amongst threads, without using synchronized. In general, you're better off using synchronizers or the java.util.concurrent framework rather than bare Atomic* unless you know what you're doing.

Two excellent dead-tree references which will introduce you to this topic:

Note that (I don't know if this has always been true) reference assignment (i.e. =) is itself atomic (updating primitive 64-bit types like long or double may not be atomic; but updating a reference is always atomic, even if it's 64 bit) without explicitly using an Atomic*.
See the Java Language Specification 3ed, Section 17.7.

Ashwanth Kumar
  • 667
  • 4
  • 10
andersoj
  • 22,406
  • 7
  • 62
  • 73
  • 56
    Correct me if I'm wrong, but it seems like the key to needing this is because you need to do a "compareAndSet". If all I needed to do was set I wouldn't need the AtomicObject at all because of reference updates themselves being atomic? – sMoZely Jan 20 '13 at 21:46
  • Is it safe to do cache.compareAndSet(cachedValue, someFunctionOfOld(cachedValueToUpdate))? I.e. inline the computation? – kaqqao Dec 10 '13 at 12:07
  • 4
    @veggen Function arguments in Java are evaluated before function itself, so inlining makes no difference in this case. Yes, it is safe. – Dmitry Feb 19 '14 at 12:54
  • 42
    @sMoZely That is correct, but if you're not using `AtomicReference` you should mark the variable `volatile` because while the _runtime_ guarantees that reference assignment is atomic, the _compiler_ may perform optimizations under the assumption that the variable was not being modified by other threads. – kbolino Mar 14 '14 at 17:51
  • @kbolino only if you change your AtomicReference field (`cache` in the code above) to a different AtomicReference. If you keep the same one, like in the answer, you shouldn't need `volatile` – Brad Cupit Mar 25 '14 at 21:54
  • 1
    @BradCupit note that I said "if you're **not** using `AtomicReference`"; if you **are** using it, then my advice would be to go in the opposite direction and mark it `final` so the compiler can optimize accordingly. – kbolino Feb 19 '15 at 02:26
  • @kbolino Whoops! You're right: not using AtomicReference it would be good to use `volatile`. And using `final` for an AtomicReference also sounds good. – Brad Cupit Feb 19 '15 at 16:38
  • Thank you for the Java Specification reference.:) – Anand Varkey Philips Sep 20 '20 at 17:52
  • 5
    Shouldn't the last line be `boolean success = cache.compareAndSet(cachedValueToUpdate, newValue);` ? – haikalpribadi Sep 24 '20 at 14:52
  • @haikalpribadi : No it should be just as it was written because compareAndSet: _Atomically sets the value to newValue if the current value == expectedValue_ So it would return _true_ so long as the cached value didn't change and it was set to the new value, if not it returns _false_ and the value currently stored is not changed (at least that's how I understood it) – jbolt Oct 30 '20 at 18:15
  • 1
    This ton of text does not answer the main post question: when to use it? Words "use it when you know what you're doing" - are not the answer. –  Mar 06 '21 at 12:17
  • 3
    @haikalpribadi Yes I'm pretty sure you are right. The `newValue` is just thrown away. The value put into the cache is the one that came out of it, `cachedValueToUpdate`. Fascinating that it took ten years and 236 upvotes to find. – Mark Jul 08 '21 at 09:13
  • @jbolt your explanation itself is correct. But that's not what the example in the answer above is quite doing. What you refer to as `expectedValue` should be `cachedValueToUpdate` in the example above, not `cachedValue`. – haikalpribadi Jul 08 '21 at 19:26
  • @Mark thanks, how can we get the post to get reviewed & updated? – haikalpribadi Jul 08 '21 at 19:29
  • 1
    I strongly disagree with `you're better off using synchronizers or the java.util.concurrent framework rather than bare Atomic* unless you know what you're doing`. Even when it *is* appropriate - atomic package classes are (1) safer since they encapsulate thread-safety code; (2) more readable, since they encapsulate thread-safety code :) and explicitly declare what they safe-guard; and (3) lock-free hence faster! So, IMHO, it should be the other way around - monitor-based `synchronization` should be the last resort, in case the use of atomic package classes aren't appropriate. – yair Aug 25 '21 at 22:29
  • Could anyone rephrase this: "Suppose you want to check to see if a specific field only if the state of the object remains as you last checked:" What do we want? – Ekaterina Nov 13 '21 at 11:22
134

An atomic reference is ideal to use when you need to update content (in an immutable object) accessed by multiple threads by replacing it with a new copy (of the immutable object) shared between these threads. That is a super dense statement, so I will break it down a bit.

First, an immutable object is an object that is effectively not changed after construction. Frequently, an immutable object's methods return new instances of that same class. Some examples include the wrapper classes Long and Double, as well as String, just to name a few. (According to Programming Concurrency on the JVM, immutable objects are a critical part of modern concurrency.)

Next, why is an AtomicReference better than a volatile object for sharing that shared value? A simple code example will show the difference.

volatile String sharedValue;

static final Object lock = new Object();

void modifyString() {
    synchronized (lock) {
        sharedValue = sharedValue + "something to add";
    }
}

Every time you want to modify the string referenced by that volatile field based on its current value, you first need to obtain a lock on that object. This prevents some other thread from coming in during the meantime and changing the value in the middle of the new string concatenation. Then when your thread resumes, you clobber the work of the other thread. But honestly that code will work, it looks clean, and it would make most people happy.

Slight problem. It is slow. Especially if there is a lot of contention of that lock Object. Thats because most locks require an OS system call, and your thread will block and be context switched out of the CPU to make way for other processes.

The other option is to use an AtomicReference.

public static AtomicReference<String> shared = new AtomicReference<>();
String init = "Inital Value";
shared.set(init);
//now we will modify that value
boolean success = false;
while (!success) {
    String prevValue = shared.get();
    // do all the work you need to
    String newValue = shared.get() + "let's add something";
    // Compare and set
    success = shared.compareAndSet(prevValue, newValue);
}

Now why is this better? Honestly that code is a little less clean than before. But there is something really important that happens under the hood in AtomicRefrence, and that is compare and swap. It is a single CPU instruction, not an OS call, that makes the switch happen. That is a single instruction on the CPU. And because there are no locks, there is no context switch in the case where the lock gets exercised which saves even more time!

The catch is, for AtomicReferences, this does not use a .equals() call, but instead an == comparison for the expected value. So make sure the expected is the actual object returned from get in the loop.

jackrabb1t
  • 14,609
  • 1
  • 21
  • 20
Erik Helleren
  • 1,481
  • 1
  • 10
  • 7
  • 15
    Your two examples behave differently. You'd have to loop on `worked` to get the same semantics. – CurtainDog Oct 13 '14 at 20:32
  • 6
    I think you should initialize the value inside the AtomicReference constructor, otherwise another thread may still see the value null before you call shared.set. (Unless shared.set is run in a static initializer.) – Henno Vermeulen May 20 '15 at 09:35
  • 10
    In your second example, you should as of Java 8 use something like: shared.updateAndGet( (x) -> (x+"lets add something")); ... which will repeatedly call the .compareAndSet until it works. That is equivalent to the synchronized block which would always succeed. You need to ensure that the lambda you pass in is side-effect-free though because it may be called multiple times. – Tom Dibble Dec 09 '15 at 17:02
  • @CurtainDog Loop to hope that `shared` might become `prevValue` again? Good luck :) – Ivan Balashov Jul 14 '16 at 06:52
  • No, typically you check the value, perform your work and then do the compare and swap all within the loop. It's a very common pattern in lock free algorithms, but I have seen it perform abysmally in certain non-obvious scenarios so TBH I tend to prefer locking solutions (if you can't avoid sharing state of course). – CurtainDog Jul 16 '16 at 20:01
  • 7
    There is not need to make volatile String sharedValue. The synchronized(lock) is good enough to establish the happen before the relationship. – Jai Pandit Jul 20 '17 at 05:26
  • 10
    "...change the state of an immutable object" is imprecise here, in part because being literal you can't change the state of an immutable object. The example demonstrates changing the reference from one immutable object instance to a different one. I realize that's pedantic but I think it's worth highlighting given how confusing Thread logic can be. – Mark Phillips Sep 23 '19 at 04:01
  • 2
    "change the state of an immutable object " - that's sounds like oksymoron. You cannot change state of immutable object, by definition. You can only change a reference to an immutable object. – Malachiasz Dec 19 '19 at 08:38
  • Excellent Answer; thank you. However, I agree that first sentence could be improved. Perhaps change "… when you need to share and change the state of an immutable object between multiple threads." to "…when you need to update content by replacing an immutable object shared between threads." – Basil Bourque May 24 '20 at 00:43
  • 2
    " change the state of an immutable object " - you're kidding, right? –  Mar 06 '21 at 12:18
  • Regarding the comments on the immutable object sentence put forth above: "an immutable object is an object that is effectively not changed after construction": is the person referring to objects which are not immutable in the strict sense but once constructed and assigned to the AtomicReference their field values are not changed - so the object is 'effectively immutable' in a narrow way. If the objects state (but not the reference stored in the AtomicRef) were to change, then the update might not be visible to other threads – Deepak Jan 05 '22 at 06:03
45

Here is a use case for AtomicReference:

Consider this class that acts as a number range, and uses individual AtmomicInteger variables to maintain lower and upper number bounds.

public class NumberRange {
    // INVARIANT: lower <= upper
    private final AtomicInteger lower = new AtomicInteger(0);
    private final AtomicInteger upper = new AtomicInteger(0);

    public void setLower(int i) {
        // Warning -- unsafe check-then-act
        if (i > upper.get())
            throw new IllegalArgumentException(
                    "can't set lower to " + i + " > upper");
        lower.set(i);
    }

    public void setUpper(int i) {
        // Warning -- unsafe check-then-act
        if (i < lower.get())
            throw new IllegalArgumentException(
                    "can't set upper to " + i + " < lower");
        upper.set(i);
    }

    public boolean isInRange(int i) {
        return (i >= lower.get() && i <= upper.get());
    }
}

Both setLower and setUpper are check-then-act sequences, but they do not use sufficient locking to make them atomic. If the number range holds (0, 10), and one thread calls setLower(5) while another thread calls setUpper(4), with some unlucky timing both will pass the checks in the setters and both modifications will be applied. The result is that the range now holds (5, 4)an invalid state. So while the underlying AtomicIntegers are thread-safe, the composite class is not. This can be fixed by using a AtomicReference instead of using individual AtomicIntegers for upper and lower bounds.

public class CasNumberRange {
    // Immutable
    private static class IntPair {
        final int lower;  // Invariant: lower <= upper
        final int upper;

        private IntPair(int lower, int upper) {
            this.lower = lower;
            this.upper = upper;
        }
    }

    private final AtomicReference<IntPair> values = 
            new AtomicReference<IntPair>(new IntPair(0, 0));

    public int getLower() {
        return values.get().lower;
    }

    public void setLower(int lower) {
        while (true) {
            IntPair oldv = values.get();
            if (lower > oldv.upper)
                throw new IllegalArgumentException(
                    "Can't set lower to " + lower + " > upper");
            IntPair newv = new IntPair(lower, oldv.upper);
            if (values.compareAndSet(oldv, newv))
                return;
        }
    }

    public int getUpper() {
        return values.get().upper;
    }

    public void setUpper(int upper) {
        while (true) {
            IntPair oldv = values.get();
            if (upper < oldv.lower)
                throw new IllegalArgumentException(
                    "Can't set upper to " + upper + " < lower");
            IntPair newv = new IntPair(oldv.lower, upper);
            if (values.compareAndSet(oldv, newv))
                return;
        }
    }
}
Steffen Harbich
  • 2,639
  • 2
  • 37
  • 71
Binita Bharati
  • 5,239
  • 1
  • 43
  • 24
  • 3
    This article is similar to your answer, but goes deep to more complicated things. It's interesting! https://www.ibm.com/developerworks/java/library/j-jtp04186/ – LppEdd Oct 08 '18 at 17:32
  • Hi! This link is broken, can you still find the available link to this article? @LppEdd – zysaaa Dec 23 '21 at 13:00
  • 1
    @zysaaa found a cached URL - https://web.archive.org/web/20201109033000/http://www.ibm.com/developerworks/java/library/j-jtp04186/ – Dinesh Babu K G May 18 '22 at 05:19
  • Is while(true) {} always required when invoking compareAndSet()? Is there a method available that loops automatically? – Dinesh Babu K G May 18 '22 at 05:20
29

You can use AtomicReference when applying optimistic locks. You have a shared object and you want to change it from more than 1 thread.

  1. You can create a copy of the shared object
  2. Modify the shared object
  3. You need to check that the shared object is still the same as before - if yes, then update with the reference of the modified copy.

As other thread might have modified it and/can modify between these 2 steps. You need to do it in an atomic operation. this is where AtomicReference can help

HamoriZ
  • 2,370
  • 18
  • 38
17

Here's a very simple use case and has nothing to do with thread safety.

To share an object between lambda invocations, the AtomicReference is an option:

public void doSomethingUsingLambdas() {

    AtomicReference<YourObject> yourObjectRef = new AtomicReference<>();

    soSomethingThatTakesALambda(() -> {
        yourObjectRef.set(youObject);
    });

    soSomethingElseThatTakesALambda(() -> {
        YourObject yourObject = yourObjectRef.get();
    });
}

I'm not saying this is good design or anything (it's just a trivial example), but if you have have the case where you need to share an object between lambda invocations, the AtomicReference is an option.

In fact you can use any object that holds a reference, even a Collection that has only one item. However, the AtomicReference is a perfect fit.

Benny Bottema
  • 11,111
  • 10
  • 71
  • 96
14

When do we use AtomicReference?

AtomicReference is flexible way to update the variable value atomically without use of synchronization. It supports lock-free thread-safe programming on single variables.

There are multiple ways of achieving Thread safety concurrent API. Atomic variables is one of them.

Lock objects support locking idioms that simplify many concurrent applications.

Executors define a high-level API for launching and managing threads.

Concurrent collections make it easier to manage large collections of data, and can greatly reduce the need for synchronization.

Atomic variables have features that minimize synchronization and help avoid memory consistency errors.

Provide a simple example where AtomicReference should be used.

Sample code with AtomicReference:

String name1 = "Ravindra";

AtomicReference<String> reference =
    new AtomicReference<String>(name1 );

String name2 = "Ravindra Babu";
boolean result = reference.compareAndSet(name1 , name2 );
System.out.println("compareAndSet result: " + result );

Is it needed to create objects in all multithreaded programs?

You don't have to use AtomicReference in all multi threaded programs.

If you want to guard a single variable, use AtomicReference. If you want to guard a code block, use other constructs like Lock /synchronized etc.

Source: docs.oracle.com

Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
6

I won't talk much. Already my respected fellow friends have given their valuable input. The full fledged running code at the last of this blog should remove any confusion. It's about a movie seat booking small program in multi-threaded scenario.

Some important elementary facts are as follows. 1> Different threads can only contend for instance and static member variables in the heap space. 2> Volatile read or write are completely atomic and serialized/happens before and only done from memory. By saying this I mean that any read will follow the previous write in memory. And any write will follow the previous read from memory. So any thread working with a volatile will always see the most up-to-date value. AtomicReference uses this property of volatile.

Following are some of the source code of AtomicReference. AtomicReference refers to an object reference. This reference is a volatile member variable in the AtomicReference instance as below.

private volatile V value;

get() simply returns the latest value of the variable (as volatiles do in a "happens before" manner).

public final V get()

Following is the most important method of AtomicReference.

public final boolean  compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

The compareAndSet(expect,update) method calls the compareAndSwapObject() method of the unsafe class of Java. This method call of unsafe invokes the native call, which invokes a single instruction to the processor. "expect" and "update" each reference an object.

If and only if the AtomicReference instance member variable "value" refers to the same object is referred to by "expect", "update" is assigned to this instance variable now, and "true" is returned. Or else, false is returned. The whole thing is done atomically. No other thread can intercept in between. As this is a single processor operation (magic of modern computer architecture), it's often faster than using a synchronized block. But remember that when multiple variables need to be updated atomically, AtomicReference won't help.

I would like to add a full fledged running code, which can be run in eclipse. It would clear many confusion. Here 22 users (MyTh threads) are trying to book 20 seats. Following is the code snippet followed by the full code.

Code snippet where 22 users are trying to book 20 seats.

for (int i = 0; i < 20; i++) {// 20 seats
    seats.add(new AtomicReference<Integer>());
}
Thread[] ths = new Thread[22];// 22 users
for (int i = 0; i < ths.length; i++) {
    ths[i] = new MyTh(seats, i);
    ths[i].start();
}

Following is the full running code.

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

public class Solution {

    static List<AtomicReference<Integer>> seats;// Movie seats numbered as per
                                                // list index

    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        seats = new ArrayList<>();
        for (int i = 0; i < 20; i++) {// 20 seats
            seats.add(new AtomicReference<Integer>());
        }
        Thread[] ths = new Thread[22];// 22 users
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new MyTh(seats, i);
            ths[i].start();
        }
        for (Thread t : ths) {
            t.join();
        }
        for (AtomicReference<Integer> seat : seats) {
            System.out.print(" " + seat.get());
        }
    }

    /**
     * id is the id of the user
     * 
     * @author sankbane
     *
     */
    static class MyTh extends Thread {// each thread is a user
        static AtomicInteger full = new AtomicInteger(0);
        List<AtomicReference<Integer>> l;//seats
        int id;//id of the users
        int seats;

        public MyTh(List<AtomicReference<Integer>> list, int userId) {
            l = list;
            this.id = userId;
            seats = list.size();
        }

        @Override
        public void run() {
            boolean reserved = false;
            try {
                while (!reserved && full.get() < seats) {
                    Thread.sleep(50);
                    int r = ThreadLocalRandom.current().nextInt(0, seats);// excludes
                                                                            // seats
                                                                            //
                    AtomicReference<Integer> el = l.get(r);
                    reserved = el.compareAndSet(null, id);// null means no user
                                                            // has reserved this
                                                            // seat
                    if (reserved)
                        full.getAndIncrement();
                }
                if (!reserved && full.get() == seats)
                    System.out.println("user " + id + " did not get a seat");
            } catch (InterruptedException ie) {
                // log it
            }
        }
    }

}    
liushuaikobe
  • 2,152
  • 1
  • 23
  • 26
sankar banerjee
  • 101
  • 1
  • 3
  • you wrote "the most important method of AtomicReference is compareAndSet". what about the "set()" method ? it's atomic as well right ? if so, why not using set? – user10239441 Feb 08 '23 at 23:30
  • compareAndSet first checks whether the value is null. Only then id is set. Otherwise not. set does not do anything like that. – sankar banerjee Jun 22 '23 at 13:25