03 - Hilos en Java

Un hilo es una secuencia de ejecución dentro de un proceso. Java puede ejecutar varios a la vez; en un servidor, cada petición se atiende en un hilo. Entender cómo se reparten y reutilizan es necesario para decidir qué estado puede guardarse y dónde.

// Con una lambda (implementa Runnable)
Thread worker = new Thread(() -> System.out.println("working"));
worker.start();

// Esperar a que termine
worker.join();

start() lanza el hilo; el método que contiene se ejecuta en paralelo con el resto del programa. join() espera su finalización.

Crear un hilo por tarea tiene coste. Un ExecutorService mantiene un pool de hilos reutilizables:

ExecutorService pool = Executors.newFixedThreadPool(10);

pool.execute(() -> System.out.println("tarea 1"));
pool.execute(() -> System.out.println("tarea 2"));

pool.shutdown();

Las tareas se encolan y los hilos del pool las ejecutan; al terminar una tarea, el hilo queda disponible para la siguiente. Los hilos se reutilizan entre tareas distintas.

Un campo static pertenece a la clase: todos los hilos ven y modifican el mismo valor.

public class SharedValue {
    private static volatile String value;

    public static void setValue(String newValue) {
        value = newValue;
    }

    public static String getValue() {
        return value;
    }
}

Si dos tareas fijan valores distintos a la vez, la segunda puede sobrescribir el valor antes de que la primera lo lea. Es una condición de carrera: el resultado depende del orden temporal en que se ejecutan los hilos.

volatile no crea un valor distinto por hilo: el campo sigue siendo único y compartido. Garantiza visibilidad (los hilos ven la última escritura), pero las operaciones siguen siendo sueltas y pueden solaparse.

ThreadLocal guarda un valor distinto para cada hilo:

public class ThreadData {
    private static final ThreadLocal<String> DATA_BY_THREAD = new ThreadLocal<>();

    public static void setData(String data) {
        DATA_BY_THREAD.set(data);
    }

    public static String getData() {
        return DATA_BY_THREAD.get();
    }

    public static void clearData() {
        DATA_BY_THREAD.remove();
    }
}

Pero el valor es por hilo, no por tarea ni por usuario. Como el pool reutiliza los hilos, el valor fijado en una tarea sigue ahí cuando el mismo hilo ejecuta la siguiente: es una fuga de datos. La solución es eliminar el valor al terminar, con remove() en un bloque finally:

try {
    // procesar la tarea usando ThreadData.getData()
} finally {
    ThreadData.clearData();
}

Todo valor guardado en un ThreadLocal debe eliminarse al terminar la tarea. Si el hilo se reutiliza, el valor se filtra a la siguiente.
  • clase/daw/si/1eval/hilos.txt
  • Última modificación: 2026/10/12 13:01
  • por cesguiro