Seguridad de hilos
Que algo no sea thread-safe (no seguro para hilos) significa que un objeto, variable o método no está preparado para ser utilizado por múltiples hilos (threads) al mismo tiempo.
Si varios hilos intentan acceder y, sobre todo, modificar ese recurso simultáneamente, el estado de los datos se corromperá, produciendo resultados impredecibles, errores intermitentes o bloqueos.
Para entenderlo mejor, vamos a ver la explicación de por qué ocurre esto, ejemplos comunes y cómo se soluciona.
Race condition
El problema más común cuando algo no es thread-safe se conoce como condición de carrera (race condition).
Imagina una operación muy sencilla, como sumar 1 a un contador: contador++. Aunque en Java parece una sola instrucción, el procesador en realidad hace tres cosas:
- Lee el valor actual del contador (ej. 5).
- Suma 1 a ese valor (5 + 1 = 6).
- Guarda el nuevo valor en la variable (6).
Si el contador no es thread-safe, y dos hilos (llamémosles A y B) intentan hacer contador++ exactamente al mismo tiempo, puede pasar lo siguiente:
- El A lee el valor: 5.
- El B lee el valor: 5 (antes de que el A haya guardado nada).
- El A suma 1 y guarda el valor: 6.
- El B suma 1 y guarda el valor: 6.
Aunque dos hilos intentaron sumar 1 (lo que debería haber resultado en 7), el resultado final es 6. Se ha perdido información porque la operación no estaba protegida.
Ejemplos comunes en Java
La biblioteca estándar de Java tiene muchas clases que no son thread-safe por defecto, ya que hacerlas seguras consume más recursos (hace que el programa vaya más lento). Es tu responsabilidad protegerlas si las vas a usar en un entorno multihilo.
- Colecciones básicas:
ArrayList,HashMap,HashSetno son thread-safe. Si un hilo está leyendo unArrayListmientras otro añade un elemento, Java probablemente lanzará una excepciónConcurrentModificationException. - Manipulación de texto:
StringBuilderno es thread-safe (por eso es muy rápido). - Fechas antiguas:
SimpleDateFormates famosamente no thread-safe, y usar la misma instancia en varios hilos causa errores desastrosos al formatear fechas.
¿Cómo se hace algo thread-safe?
Si necesitas que varios hilos trabajen con la misma información, tienes varias herramientas en Java para evitar el caos:
- Sincronización (
synchronized): Puedes usar esta palabra clave en métodos o bloques de código. Esto crea un "candado" (lock) que obliga a los hilos a hacer fila; solo un hilo puede ejecutar ese código a la vez. - Clases Atómicas: Java ofrece clases en el paquete
java.util.concurrent.atomic(comoAtomicIntegeroAtomicBoolean) que realizan operaciones como elcontador++de forma segura en un solo paso indivisible. - Colecciones Concurrentes: En lugar de
HashMapoArrayList, puedes usarConcurrentHashMapoCopyOnWriteArrayList, que están diseñadas específicamente para funcionar perfectamente con múltiples hilos. - Alternativas seguras: Usar
StringBufferen lugar deStringBuilder, o las nuevas clases de fechas de Java 8 (java.time.LocalDate,DateTimeFormatter) que son inmutables y, por tanto, siempre thread-safe.