Declaring Methods  «Prev 


Atomic Methods (Modern Java)

Critique of the legacy text. The original page mixed Java 1.4–era “atomic method lists” with brief one-line descriptions, didn’t explain why atomics exist, and equated “blocked on I/O or sync” with all forms of blocking. It also omitted today’s most useful types (LongAdder, LongAccumulator) and modern guidance on when to choose atomics vs. locks. This rewrite centers on intent, correctness, and trade-offs, with small, runnable examples.

Use the java.util.concurrent atomic classes and lock framework to implement correct, scalable concurrency

What “atomic” means

Atomic variables perform updates as a single, indivisible action using CAS (compare-and-set) under the hood. CAS avoids heavyweight locking, making single-variable updates fast and contention-friendly. Atomic reads/writes have happens-before semantics like volatile.

Core classes you’ll actually use

Why non-atomic "++" is a trap

Even a single line like count++ is a read-modify-write sequence and is not atomic. Multiple threads can interleave and lose increments.

public class Counter {
  private int count;                 // not atomic
  public void increment() { count++; } // data race
  public int getValue() { return count; }
}


Fix with AtomicInteger

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicCounter {
  private final AtomicInteger count = new AtomicInteger();

  public void increment() {
    count.incrementAndGet();   // atomic RMW
  }
  public int getValue() {
    return count.get();        // atomic read
  }
}

High contention? Prefer LongAdder

LongAdder spreads updates across cells and aggregates on read, trading a slightly slower read for much faster write under contention.

import java.util.concurrent.atomic.LongAdder;

public class Metrics {
  private final LongAdder requests = new LongAdder();
  public void recordRequest() { requests.increment(); }
  public long snapshot()      { return requests.sum(); }
}

CAS patterns with compareAndSet

Use compareAndSet(expected, update) to implement lock-free state transitions.

import java.util.concurrent.atomic.AtomicReference;

enum State { NEW, RUNNING, TERMINATED }

public class LifecycleFlag {
  private final AtomicReference<State> state = new AtomicReference<>(State.NEW);

  public boolean start() {
    return state.compareAndSet(State.NEW, State.RUNNING);
  }

  public boolean stop() {
    return state.getAndSet(State.TERMINATED) != State.TERMINATED;
  }
}


Lazy set versus set

lazySet(v) (a.k.a. release set) eventually publishes a value with weaker ordering; set(v) publishes immediately with standard volatile semantics. Use lazySet when a slight delay is harmless (e.g., flags). For most code, set is clearer.

When to choose locks instead

Atomics shine for single-variable state and simple transitions. Once you must update multiple fields consistently or guard complex invariants, use locks:

import java.util.concurrent.locks.*;

public class PriceBook {
  private final ReadWriteLock rw = new ReentrantReadWriteLock();
  private double price;

  public void setPrice(double p) {
    rw.writeLock().lock();
    try { price = p; } finally { rw.writeLock().unlock(); }
  }

  public double getPrice() {
    rw.readLock().lock();
    try { return price; } finally { rw.readLock().unlock(); }
  }
}

Exam-oriented quick reference

APIWhat it does
get(), set(v)Atomic read/write with volatile semantics.
getAndIncrement(), incrementAndGet()Atomic ++ with old/new return.
getAndAdd(Δ), addAndGet(Δ)Atomic add; use LongAdder if many threads contend.
compareAndSet(expect, update)CAS transition; loop/retry on failure.
getAndSet(v)Swap atomically, returning the previous value.
lazySet(v)Eventually visible release write; weaker ordering.

Takeaways