====== 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.