Codectionary / Developer documentation / Java

Atomic Variables

Classes like AtomicInteger, AtomicLong, and AtomicBoolean, from java.util.concurrent.atomic, provide thread-safe operations on single variables without needing explicit synchronized blocks. They use low-level, lock-free CPU instructions (compare-and-swap) to guarantee that operations like incrementAndGet() are atomic - performed as a single, uninterruptible step - even when many threads use them concurrently.

Syntax

AtomicInteger counter = new AtomicInteger(0);

Examples

AtomicInteger Basics

Thread-safe increment and decrement without a synchronized block.

import java.util.concurrent.atomic.AtomicInteger;

AtomicInteger counter = new AtomicInteger(0);

counter.incrementAndGet();  // atomically adds 1, returns new value
counter.incrementAndGet();
counter.addAndGet(5);        // atomically adds 5

System.out.println(counter.get());  // 7

Comparing to a Non-Atomic Counter

Why AtomicInteger matters: a plain int shared across threads is not safe to increment without extra synchronization.

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

AtomicInteger safeCounter = new AtomicInteger(0);
ExecutorService executor = Executors.newFixedThreadPool(4);

for (int i = 0; i < 1000; i++) {
    executor.submit(safeCounter::incrementAndGet);
}

executor.shutdown();
// safeCounter will reliably reach 1000 - a plain "int count" shared this way
// could lose increments due to race conditions between threads

compareAndSet()

The atomic building block: only updates the value if it currently matches an expected value.

import java.util.concurrent.atomic.AtomicInteger;

AtomicInteger value = new AtomicInteger(10);

boolean updated = value.compareAndSet(10, 20);  // succeeds: current value was 10
System.out.println(updated);      // true
System.out.println(value.get());   // 20

boolean failed = value.compareAndSet(10, 30);   // fails: current value is now 20, not 10
System.out.println(failed);       // false
System.out.println(value.get());   // still 20

Best practices

  • Use atomic classes instead of a synchronized block when you only need to protect a single variable - they are typically faster due to lock-free implementation
  • Use incrementAndGet()/getAndIncrement() rather than manually reading, adding, and writing back a value across separate atomic operations
  • Reach for a synchronized block or a Lock instead when you need to atomically update more than one related variable together
  • Remember atomic classes only guarantee safety for the operations they provide directly - wrapping several atomic calls together does not make the whole sequence atomic

At a glance

Purpose
General-purpose application development
File extension
.java
Runs in
Java Virtual Machine
Usually used with
JDK and Java libraries

Specifications & further reading

Related Java documentation

Creating Threads (Thread & Runnable)
A thread is an independent path of execution within a program, letting multiple tasks run seemingly simultaneously. Java offers two main ways to create a thread: extending the Thread class directly and overriding its run() method, or implementing the Runnable interface and passing it to a Thread - the Runnable approach is generally preferred, since Java doesn't support multiple inheritance and Runnable keeps your class free to extend something else.
synchronized Keyword
The synchronized keyword prevents multiple threads from executing a block of code (or a method) at the same time on the same object, protecting shared, mutable data from race conditions. When one thread enters a synchronized block, it acquires a lock on the specified object; any other thread trying to enter a synchronized block on that same object must wait until the lock is released.
ExecutorService & Thread Pools
ExecutorService, from java.util.concurrent, manages a pool of reusable threads, letting you submit tasks without manually creating and managing individual Thread objects. This avoids the overhead of constantly creating new threads and gives you control over how many run concurrently. Executors provides convenient factory methods for common pool configurations, like a fixed-size pool or a single-threaded executor.
Thread Control: sleep, join, interrupt
Java provides several methods for coordinating and controlling thread execution. Thread.sleep() pauses the current thread for a specified duration. join() makes the calling thread wait until another thread finishes before continuing. interrupt() signals a thread that it should stop what it's doing, a cooperative mechanism the target thread must actively check for and respond to.