====== 11 - Hilos en Tomcat ====== Un servidor web atiende muchas peticiones a la vez. En **Tomcat**, cada petición la procesa un **hilo**; entender cómo se reparten y reutilizan es necesario para decidir qué estado puede guardarse y dónde. ===== 1. Cada petición se atiende en un hilo ===== **Tomcat** mantiene un **pool de hilos** con hilos **worker** disponibles. Cuando llega una petición **HTTP**, se asigna a un hilo libre; al terminar la respuesta, el hilo vuelve al pool y puede atender otra petición. ^ Peticiones ^ En qué hilos se ejecutan ^ | Simultáneas | Cada una se ejecuta en un hilo distinto | | Sucesivas | Pueden ejecutarse en el mismo hilo reutilizado | @startuml actor "Cliente A" as A actor "Cliente B" as B actor "Cliente C" as C participant "Tomcat" as T participant "Hilo 1" as H1 participant "Hilo 2" as H2 A -> T : Petición A T -> H1 : la atiende el hilo 1 B -> T : Petición B (simultánea) T -> H2 : la atiende el hilo 2 H1 --> A : respuesta H2 --> B : respuesta C -> T : Petición C (posterior) T -> H1 : reutiliza el hilo 1 H1 --> C : respuesta @enduml ===== 2. Estado compartido entre hilos ===== 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 peticiones fijan valores distintos a la vez, la segunda puede sobrescribir el valor antes de que la primera lo lea: una petición trabaja con el valor de otra. Es una **condición de carrera**: el resultado depende del orden temporal en que se ejecutan los hilos. ''volatile'' no crea un valor distinto para cada hilo: el campo ''static'' sigue siendo único y compartido. La diferencia es qué garantía hay al leer escrituras de otros hilos: Hilo A: escribe "A" Hilo B: escribe "B" Hilo A: lee el campo * **Con ''volatile'':** si la escritura de B ocurre antes de la lectura de A, A ve "B". La escritura es visible, pero el campo sigue siendo compartido. * **Sin ''volatile'':** Java no garantiza que A vea la escritura reciente de B. Puede observar un valor antiguo; no se puede asumir que verá "A" ni que cada hilo conserve un valor propio. En ambos casos hay acceso compartido. ''volatile'' garantiza **visibilidad**, pero no evita que una escritura sobrescriba otra ni hace indivisible una secuencia de operaciones. Por tanto, no evita la **condición de carrera**. ^ Campo ''static'' ^ Variable local ^ | Un único valor visible para todos los hilos | Cada hilo tiene la suya en su propia pila | | Requiere cuidado ante accesos concurrentes | No se comparte entre peticiones | ===== 3. ThreadLocal: un valor por hilo, no por petición ===== ''ThreadLocal'' guarda un valor distinto para cada hilo: cada hilo solo ve el suyo. 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 petición. Como Tomcat reutiliza los hilos, el valor fijado durante una petición sigue ahí cuando el mismo hilo atiende la siguiente: la nueva petición encontraría un valor que no le corresponde. Es una **fuga** de datos entre peticiones.