May 23

Cuidado al sincronizar Boolean

 Depurando código de otra persona, encontré que hacía synchronized(variable) siendo variable un Boolean con mayúscula. Me llamó la atención de que dentro del bloque synchronized cambiaba el valor de la variable, algo así

synchronized(variable) {
   …
   variable = false;
   …
}

Me sonaba mal. Eso de cambiar la variable con la que estás sincronizando no parece limpio. Si otro hilo intenta sincronizar con la variable, puede estar sincronizando con otro objeto distinto y la idea no suele ser esa. De todas formas, decidí hacer una búsqueda en google y me encontré con esta pequeña joya de por qué no debemos sincronizar con Boolean.

Por un lado menciona lo que acabo de comentar, no es buena idea cambiar dentro (ni en general en ningún lado), el objeto sobre el que se está sincronizando. Si lo cambiamos, nunca tendremos garantía de que los hilos estén sincronizando sobre el mismo objeto, que es el objetivo de un synchronized. El objeto debe ser fijo. El enlace da varias alternativas correctas, como crear un Object lock = new Object() fijo para sincronizar con él o ver si nos valdría un AtomicBoolean con el que no tendríamos que sincronizar 

Sin embargo, lo que realmente me ha llamado la atención, es el comentario de que el Boolean es además especialmente problemático. Cuando hacemos esto

Boolean variable = false;

Java hace una conversión automática de false a Boolean.FALSE, ya que la variable es Boolean con mayúscula. Y java es listo, así que no crea instancias de Boolean.FALSE según hacen falta, sino que siempre reutiliza un mismo Boolean.FALSE. Y ahí viene el problema. ¿Qué pasa si sincronizamos dos grupos de hilos que son independientes cada uno con una variable Boolean que no tiene que ver con la otra?. Pues que podemos tener problemas porque sólo hay un objeto en todo java Boolean.TRUE y solo uno Boolean.FALSE, así que esos hilos que no tienen nada que ver están en realidad sincronizándose sobre el mismo objeto. En el siguiente código

// Hilo 1
Boolean unaVariable = true;
synchronized (unaVariable) {
   …
}

// Hilo 2 que no tiene nada que ver con Hilo 1
Boolean otraVariableQueNoTieneNadaQueVerConUnaVariable = true;
synchronized (otraVariableQueNoTieneNadaQueVerConUnaVariable) {
   …
}

hay dos hilos que no tienen que ver el uno con el otro y usan variables que no tienen que ver la  una con la otra….. están sicronizándose sobre el mismo y único objeto Boolean.TRUE que hay en java. Como mínimo, es una espera inútil. En el peor de los casos, puede dar lugar a abrazos mortales.

 

Entradas relacionadas:

Leave a Reply