Atomic Variables in Java

Naveen AnamaneniUpdated · 4 min read

Atomic variables, in java.util.concurrent.atomic, are classes that hold a single value and update it atomically, as one indivisible step, without locks. AtomicInteger.incrementAndGet() does the whole "read, add one, write" safely even when many threads call it at once, which a plain count++ can't. The main classes are AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference and the high-throughput LongAdder. In this lesson, you will learn their methods and when to use each.

The Main Atomic Classes

ClassHolds
AtomicInteger, AtomicLongAn int or long
AtomicBooleanA boolean
AtomicReference<V>A reference to an object
AtomicIntegerArray, AtomicLongArrayArrays whose elements update atomically
LongAdder, DoubleAdderSums that many threads add to at once

Methods of AtomicInteger

MethodDescription
get() / set(v)It is used to read or write the value.
incrementAndGet() / getAndIncrement()It adds one and returns the new or the old value.
addAndGet(delta)It adds delta and returns the new value.
compareAndSet(expected, newValue)It sets the value only if it currently equals expected.
updateAndGet(fn)It applies a function atomically and returns the result.
accumulateAndGet(x, fn)It combines the value with x atomically.

Example: Counters and Updates

Let's see an example where four threads update shared atomic values.

Atomics.java▶ Try it
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.LongAdder;
 
public class Atomics {
    public static void main(String[] args) {
        AtomicInteger visits = new AtomicInteger();
        AtomicInteger highest = new AtomicInteger();
        LongAdder clicks = new LongAdder();
 
        try (ExecutorService pool = Executors.newFixedThreadPool(4)) {
            for (int t = 0; t < 4; t++) {
                int base = t * 10;
                pool.submit(() -> {
                    for (int i = 0; i < 25_000; i++) {
                        visits.incrementAndGet();
                        clicks.increment();
                    }
                    highest.accumulateAndGet(base + 7, Math::max);
                });
            }
        }
        System.out.println("Visits: " + visits.get());
        System.out.println("Clicks: " + clicks.sum());
        System.out.println("Highest score: " + highest.get());
 
        AtomicReference<String> leader = new AtomicReference<>("Navika");
        boolean changed = leader.compareAndSet("Navika", "Srihitha");
        boolean changedAgain = leader.compareAndSet("Navika", "Advitha");
        System.out.println("Leader: " + leader.get()
            + " (changes: " + changed + ", " + changedAgain + ")");
    }
}
Output
Visits: 100000
Clicks: 100000
Highest score: 37
Leader: Srihitha (changes: true, false)

Explanation: Four threads each add 25,000 to visits and clicks, and no update is lost: both reach 100,000. accumulateAndGet(x, Math::max) safely keeps the largest value any thread offered (the threads offered 7, 17, 27 and 37). compareAndSet("Navika", "Srihitha") succeeds because the leader really was Navika; the second call fails because the leader is no longer Navika, so Advitha's change is refused.

AtomicLong vs LongAdder

AtomicLongLongAdder
StorageOne valueSeveral cells, added up on sum()
Under heavy contentionThreads retry often, slowerThreads update different cells, much faster
Exact current valueget() is exactsum() is exact only when no updates are happening
compareAndSetYesNo
Best forIDs, sequence numbers, values you compareStatistics, counters many threads increment

Common Mistakes

Calling get() then set(get() + 1). That's two steps again; use incrementAndGet().

Using LongAdder where you need compareAndSet or an exact snapshot. Use AtomicLong.

Expecting atomics to coordinate several variables. Each atomic covers only itself.

Interview Corner

What are atomic variables, and how do they work?

Atomic classes such as AtomicInteger, AtomicLong and AtomicReference hold a value and offer lock-free, thread-safe operations like incrementAndGet, compareAndSet and updateAndGet. They're built on the CPU's compare-and-swap instruction: an update succeeds only if the value hasn't changed since it was read, otherwise it retries. They also have volatile semantics, so changes are visible to all threads. For heavily contended counters, LongAdder spreads updates across cells for better throughput.

Practice

  1. Easy: Replace a synchronized ID generator with an AtomicLong and generate ten IDs from three threads.
  2. Harder: Keep a "best score and its player" pair in an AtomicReference of a record, and update it from several threads with updateAndGet.

Quick check: Atomic variables

4 questions
1Which method safely adds one and returns the new value?
2When does compareAndSet(a, b) succeed?
3Which class is fastest for a counter many threads increment?
4Do atomic classes use locks?

Key Takeaways

Was this tutorial helpful?

Lesson 420 of 482