8 - Operaciones atómicas (java.util.concurrent.atomic)

Las clases atómicas proveen operaciones indivisibles sin bloqueos en hardware moderno. Son ideales para contadores, flags y referencias simples.

8.1 ¿Qué es una operación atómica?

  • Indivisible: no puede interrumpirse ni observarse a medio camino.
  • Crucial para contadores: evita condiciones de carrera en incrementos.
  • En estructuras simples: banderas, referencias y versiones ligeras.

8.2 Tipos atómicos

  • AtomicInteger
  • AtomicLong
  • AtomicBoolean
  • AtomicReference<T>
  • AtomicStampedReference<T> (previene ABA con sello/versión: detecta cuando un valor cambia A→B→A y evita aceptarlo por error).

8.3 Métodos importantes

  • getAndIncrement() / incrementAndGet(): incrementos atómicos con retorno previo o posterior.
  • compareAndSet() (CAS): cambia el valor si coincide con el esperado.
  • lazySet(): publica el valor con ordenamiento relajado (puede retrasar visibilidad).
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

public class ContadorAtomicoDemo {
  private static final AtomicInteger contador = new AtomicInteger(0);
  private static final AtomicStampedReference<String> ref =
      new AtomicStampedReference<>("v1", 1);

  public static void main(String[] args) throws InterruptedException {
    Runnable tarea = () -> {
      for (int i = 0; i < 1_000; i++) {
        contador.incrementAndGet();
      }
    };

    Thread t1 = new Thread(tarea, "t1");
    Thread t2 = new Thread(tarea, "t2");
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println("Contador atómico: " + contador.get()); // 2000

    int[] stampHolder = new int[1];
    String valor = ref.get(stampHolder);
    int version = stampHolder[0];
    boolean actualizado = ref.compareAndSet("v1", "v2", version, version + 1);
    System.out.println("CAS con sello: " + actualizado + " valor=" + ref.getReference() +
        " version=" + ref.getStamp());
  }
}

El ejemplo usa dos hilos que incrementan un AtomicInteger sin locks y muestran el valor final esperado (2000). Luego obtiene el sello actual de AtomicStampedReference y aplica compareAndSet verificando tanto el valor como la versión para evitar el problema ABA (cambios A→B→A). Si coinciden, actualiza a "v2" y aumenta el sello.

8.4 Ventajas del enfoque atómico

  • Sin bloqueos: muchas operaciones no requieren lock.
  • Rápido: suele superar a synchronized en contadores y flags.
  • Ideal: contadores de métricas, banderas de estado, referencias intercambiables.

8.5 Limitaciones

  • No para invariantes complejos: múltiples campos correlacionados requieren locks.
  • No reemplazan locks: estructuras compuestas siguen necesitando exclusión mutua.
  • CAS con contención: puede fallar repetidamente bajo alta carga, generando spinners lentos.