synchronized es una palabra clave que permite realizar un acceso seguro a hilos a secciones críticas de código. Puede usarse tanto para un método (por ejemplo, public synchronized void foo()) como para un bloque de código (synchronized(obj) { ... }). Cuando un hilo entra en un bloque synchronized, obtiene el monitor (lock) del objeto. Mientras el monitor esté ocupado, otros hilos no pueden entrar en otro bloque synchronized que use el mismo objeto como monitor.
Características:
this, sobre un objeto estático (por ejemplo, sobre la clase), o sobre un objeto arbitrario.public class Counter { private int count; private final Object lock = new Object(); // objeto privado public void increment() { synchronized(lock) { count++; } } }
¿Por qué es mejor usar un lock privado? Porque si se sincroniza sobre un objeto público (por ejemplo, sobre this o sobre una cadena pública), el código externo también puede capturar ese monitor, lo que lleva a bloqueos (deadlocks) o un funcionamiento incorrecto.
Pregunta: ¿Qué sucederá si se sincroniza sobre un objeto de tipo String que contiene un valor fijo?
Respuesta: Las cadenas en Java son internadas (los mismos objetos para literales idénticos). Si se sincroniza sobre una cadena como synchronized("lock"), se puede cruzar accidentalmente con otro código que se sincroniza sobre el mismo literal, lo que resultará en un bloqueo inesperado entre partes completamente diferentes del programa.
synchronized("LOCK") { ... }
Historia
En un sistema de trading multihilo, se utilizaron objetos públicos para sincronización, y el código externo pudo capturar el lock, lo que llevó a deadlocks temporales entre hilos de diferentes módulos y tiempos de inactividad en la bolsa.
Historia
Un joven desarrollador sincronizó el acceso a una colección sobre un literal de cadena. Otra parte del código también se sincronizó sobre una cadena con el mismo valor. Estos hilos se pusieron en cola uno tras otro, lo que provocó una fuerte desaceleración de la lógica de negocio.
Historia
Para la sincronización, se eligió cada vez un nuevo objeto:
synchronized(new Object()) { ... }. Como resultado, la sincronización no funcionó en absoluto, y diferentes hilos accedieron simultáneamente a los datos. Esto solo se descubrió en las pruebas de carga.