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