← Back to Advanced Java Course | Chapter 11: Advanced & Security | Lesson 8 of 19

Java Atomic Classes

Why Atomic Classes?

Atomic classes, defined in java.util.concurrent.atomic, provide lock-free, thread-safe operations on a single variable. They rely on CPU-level Compare-And-Swap (CAS) instructions to update values safely, avoiding the overhead of acquiring and releasing a traditional lock for simple updates.

Example: Why Atomic Classes?

java
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
	public static void main(String[] args) {
		AtomicInteger counter = new AtomicInteger(0); // lock-free, CAS-based updates
		counter.incrementAndGet();
		System.out.println(counter.get());
	}
}

AtomicInteger Basics

AtomicInteger provides thread-safe increment and decrement operations out of the box -- a method like incrementAndGet() does the same job as ++i, but does it atomically, so concurrent threads incrementing the same counter never lose an update to a race condition.

Example: AtomicInteger Basics

java
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
	public static void main(String[] args) throws InterruptedException {
		AtomicInteger counter = new AtomicInteger(0);
		Runnable task = () -> { for (int i = 0; i < 1000; i++) counter.incrementAndGet(); };
		Thread t1 = new Thread(task), t2 = new Thread(task);
		t1.start(); t2.start();
		t1.join(); t2.join();
		System.out.println(counter.get()); // reliably 2000
	}
}

AtomicBoolean Toggles

compareAndSet(expected, update) is the core operation behind AtomicBoolean: it only writes the new value if the variable's current value still matches what you expected, which is how CAS-based classes detect and reject conflicting concurrent updates without ever blocking.

Example: AtomicBoolean Toggles

java
import java.util.concurrent.atomic.AtomicBoolean;
public class Main {
	public static void main(String[] args) {
		AtomicBoolean flag = new AtomicBoolean(false);
		boolean changed = flag.compareAndSet(false, true); // only writes if current value matches expected
		System.out.println(changed + " " + flag.get());
	}
}

AtomicReference for Objects

AtomicReference lets you update an object reference atomically and safely across threads, which is useful for managing shared configuration objects, immutable state snapshots, or any mutable-reference field you'd otherwise need a lock to protect.

Example: AtomicReference for Objects

java
import java.util.concurrent.atomic.AtomicReference;
public class Main {
	public static void main(String[] args) {
		AtomicReference<String> config = new AtomicReference<>("v1");
		config.set("v2"); // atomic, safe across threads
		System.out.println(config.get());
	}
}

Performance of Atomic Operations

Atomic operations never block a thread the way a traditional lock can, so under low-to-moderate contention they run faster and use fewer CPU resources than synchronized locking. Under very high contention, though, repeated CAS retries can still degrade performance, so they aren't a universal replacement for locks.

Example: Performance of Atomic Operations

java
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
	public static void main(String[] args) {
		AtomicInteger counter = new AtomicInteger(0);
		long start = System.nanoTime();
		for (int i = 0; i < 100000; i++) counter.incrementAndGet(); // no blocking under low contention
		System.out.println("Took " + (System.nanoTime() - start) + "ns");
	}
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.