====== 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.
===== 1. Crear y ejecutar =====
// 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.
===== 2. Pool de hilos =====
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.
===== 3. Estado compartido =====
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.
===== 4. ThreadLocal: un valor por hilo =====
''ThreadLocal'' guarda un valor distinto para cada hilo:
public class ThreadData {
private static final ThreadLocal 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.