Estamos teniendo unos días un poco estresantes últimamente pero madrugaré este domingo para hacer el articulo de las licencias de Oracle o dejarlo próximo a terminar al menos. Conocerás de primera mano lo que es un UFO, que Named User Plus no es un insulto o que los procesadores de Intel valen la mitad… Permaneced atentos a vuestras pantallas.
Archivo del Autor: sbgermanm
Volatile, o como evitar usar bloqueos en concurrencia
En este articulo voy a explicar que es, para que sirve y cuando y como usar la palabra reservada «volatile«.
Qué es volatile
Creo que puedo resumirlo en lo siguiente: volatile es la forma adecuada de publicar un objeto inmutable sin usar mecanismos de bloqueo.
¿Por qué se necesita usar?
Porque la JVM hace lo que le viene en gana con una variable compartida y un cambio en una variable compartida por un hilo puede ser visto por otros hilos o no.
Esto es debido a que, por optimización, la JVM puede hacer cache de la variable. O no hacerlo.
Si hace cache, el cambio de un hilo en una variable compartida no sera visto por otros hilos. Cada hilo tendrá su copia local de la variable.
En principio, la JVM hace la copia local cuando intuye que la variable compartida no va a ser modificada. Para evitarlo, podemos usar la palabra reservada volatile, para indicar que esa variable es modificable, que NO haga una copia local. Si queremos indicar lo contrario y forzar la copia local (cada hilo tenga siempre su copia de la variable compartida y que el resto de hilos no vean los cambios que otros hilos hagan en ella), usaremos el antónimo, threadlocal, con lo que la variable será exclusiva de cada hilo.
La concurrencia siempre dando problemas…
Si, así es. Tenemos que manejar dos conceptos para no tener problemas cuando tenemos concurrencia:
- Asegurar la exclusión mutua, que significa asegurar que solo un hilo tiene acceso a una variable compartida en un momento dado.
- Asegurar la visibilidad, que significa asegurar que un cambio en una variable compartida realizado por un hilo es visible al resto de hilo.
La forma típica de asegurar estas dos características es establecer una región crítica (bloque de instrucciones delicado) mediante un bloqueo de acceso a la misma controlado con un monitor o testigo que solo puede ser poseído por un hilo cada vez, que pasa a ser el único hilo capaz de ejecutar esa región critica (ahora recuerdo que todo esto es parte de una serie de artículos sobre concurrencia que aún no he pasado al blog…)
La palabra mágica, volatile
volatile tiene que ver con el segundo concepto que necesitamos manejar en concurrencia, la visibilidad. Es un indicador para el compilador y los hilos para que no hagan copia local de una variable compartida y siempre lean su valor de la memoria principal.
Pero como siempre en concurrencia, hay que tener mucho cuidado. Solo implica eso. No implica en ningún caso atomicidad en las asignaciones realizadas en la variable.
Las variables volatile comparten la característica de visibilidad de synchronized, pero ninguna de las características de atomicidad.
Entonces, porqué usar volatile en vez de un bloqueo
Pues por dos razones: simplicidad y escalabilidad (volatile no puede causar un bloqueo de un hilo).
Un bloqueo nos da visibilidad de cambios pero es muy pesado. Es serializar nuestro programa diseñado para correr en hilos paralelos. volatile no implica un bloqueo.
Veamos algo simple:
boolean parar=false; //....unas lineas de código mas tarde.... while (!parar) {/*algo*/}
Supongamos varios hilos ejecutando un código parecido a ese. En un momento dado queremos para la ejecución cambiando la variable compartida parar a true. Esto se realiza desde otro hilo:
//hilo de control parar = true;
Si no hacemos nada, existen posibilidades de que ese cambio no sea visto por el resto de hilos y estos no paren su ejecución.
Si usamos un bloqueo en while conseguimos ver el cambio pero serializamos todo nuestro programa en ese punto. Con volatile no existe bloqueo, solo aseguramos que están leyendo la misma variable.
Vale, ¿eso es todo? ¿Hemos terminado?
No, no iba a ser tan simple. Otra característica del volatile es que establece una relación happens-before entre la escritura de la variable y su lectura.
Wikipedia explica mejor que yo que es una relación happens-before:
In Java specifically, a happens-before relationship is a guarantee that memory written to by statement A is visible to statement B, that is, that statement A completes its write before statement B starts its read.
Para ello, uno de las efectos es evitar reordenar el código. El compilador tiene la potestad de reordenar el código para conseguir una ejecución mas rápida del mismo pero en concurrencia existen condiciones de carrera que hace que esto provoque un desastre.
Por ejemplo:
final class Publisher { public static Publisher published; int num; Publisher(int number) { // Initialization this.num = number; // ... published = this; } }
En ese código, el compilador puede reordenar y publicar el this antes de construirlo. Para evitarlo solo tenemos que añadir volatile a la declaración de la variable published.
Veamos otro ejemplo, este de stackoverflow, teniendo esta clase:
public class SO { private volatile String a; private volatile String b; public SO() { a = null; b = null; } public void setBothNonNull( @NotNullfinalString one, @NotNullfinalString two ) { a = one; b = two; } public String getA() { return a; } public String getB() { return b; } }
En este código se asigna primero a y despues se asigna b. Si hacemos lo siguiente:
doIt() { if ( so.getB() != null ) { System.out.println( so.getA().length ); }
Nunca debería lanza un null pointer, pero eso solo se asegura por que hemos puesto volatile en la declaración de las variable. Sin el volatile, el compilador podría reordenar y, si se ejecutan a la vez doIt y setBothNonNull, doIt podría encontrar asignada solo b.
Aún hay más, efectos en otras variables…
Si tenemos una variable volatile, por ejemplo, pepe, si un hilo escribe en pepe, los valores de las variables previas a pepe que eran visibles a para ese hilo, también será visibles para el resto de los hilos. Así en este ejemplo:
public class Test { volatile static private int a; static private int b; public static void main(String [] args) throws Exception { for (int i = 0; i < 100; i++) { new Thread() { @Override public void run() { while (a==0) { } if (b == 0) { System.out.println("error"); } } }.start(); } b = 1; a = 1; } }
nunca debería ocurrir que se imprimiese «error» porque cuando en el hilo principal se pone a = 1, ya hemos puesto b = 1 y gracias a volatile, este valor siempre será visible al resto de hilos. (en realidad hay un bug, arreglado a partir de la Java 7u6 build b14 que permite que pueda ocurrir, ver stackoverflow)
Que es lo que no aporta volatile
Como he indicado antes, no implica atomicidad. No es suficiente para implementar un contador (x++ no es atómico).
Podemos usar el ejemplo de la cuenta bancaria:
public class BankAccount { private volatile int value; public int getValue() { return value; } public int increment() {return value++;} }
Si hacemos que la variable sea volatile, en teoría, como otros hilos ven el incremento, debería funcionar. En la practica, el cambio lo ven, pero el «++» son varias operaciones y mientras se realizan, se «cuelan» otros hilos y cambian el valor antes de que se realice la asignación final.
Podría ser valido si solo un hilo escribe en la variable, por lo que podríamos estar tentados de usar la chollo-sincronizaciión:
public class CheesyCounter { private volatile int value; public int getValue() { return value; } public synchronized int increment() { return value++; } }
Funciona, pero es mejor usar un read-write lock (un día de esto escribiré el articulo en el que cuento esto).
En general, volatile no puede usarse cuando un nuevo valor depende de uno antiguo ni tampoco cuando el valor de una variable depende del valor de otra variable.
Como podemos usar volatile
Status flag
Lo hemos visto antes, el típico «sigue hasta que te diga que pares»:
.... volatile boolean shutdownRequested; ... public void shutdown() { shutdownRequested = true; } public void doWork() { while (!shutdownRequested) { // do stuff } }
Simple, ¿verdad?
Veamos algo un poco mas delicado:
public class UserManager { public volatile String lastUser; public boolean authenticate(String user, String password) { boolean valid = passwordIsValid(user, password); if (valid) { User u = new User(); activeUsers.add(u); lastUser = user; } return valid; } }
En este pequeño ejemplo hay una pequeña maldad escondida. Podemos publicar sin problema quien es el ultimo usuario cambiando el valor de lastUser cada vez que se autentica un nuevo usuario y todos los hilos verán siempre el ultimo usuario. Ahora bien, si un hilo hace lastUser.setUser(«nuevo_nombre»), el resto de los hilos no es seguro que vean ese cambio.
Repito: volatile es la forma adecuada de publicar un objeto inmutable sin usar mecanismos de bloqueo.
Mas usos
- Leer y escribir variables long y double de forma atómica.
Son variables de 64 bits y la lectura de escritura de forma atómica es dependiente de la plataforma. En algunos casos se realiza en dos pasos, escribiendo 32 bits cada vez, por lo que en concurrencia puede provocar errores, al ser posible que un hilo vea un valor intermedio. Usando volátile la escritura y lectura de variables es atómica en java.
- double checked locking en Singleton
public class Singleton{ private static volatile Singleton _instance; //volatile variable public static Singleton getInstance(){ if(_instance == null){ synchronized(Singleton.class){ if(_instance == null) _instance = new Singleton(); } } return _instance; }
Puede ocurrir que entren dos hilos, uno llega al new y cambia el valor de _instance pero el otro hilo no lo ve, entra en el sincro y hace otro new.
Además, existe el riesgo de ver una referencia actualizada pero que apunte a un construido parcialmente.
Con volatile asegurarnos la visibilidad de ese cambio.
No obstante, el Singleton se implementa mejor con un enum o un static factory method.
- Evitar que otros hilos vea objetos parcialmente construidos si estos objetos son inmutables o thread-safe
public class Cargador { public volatile ObjetoInmutable inmutable; public void initInBackground() { inmutable = new ObjetoInmutable(); } } public class otraClase{ public void doWork() { while (true) { ... if (cargador.inmutable != null) metodoxxx(cargador.inmutable); } } }
Si la referencia inmutable no fuese volatile, doWork() podría estar viendo un objeto ObjetoInmutable parcialmente construido. Requiere que el objeto sea inmutable o thread-safe.
Y eso es todo?
Si, básicamente aquí acaba este articulo. Se pueden buscar mas patas al gato, pero no es el objetivo de este articulo. Así que, hasta el próximo…
Posdata
Alguna cosilla conectada con esto, aprovecho para escribirla por aqui:
- La JMM (java memory model) garantiza que cualquier atributo final de un objeto será totalmente inicializado antes de que publicación del objeto sea visible.
class Foo { private volatile Helper helper; public Helper getHelper() { return helper; } public void initialize() { helper = new Helper(42); } } // Immutable Helper public final class Helper { private final int n; public Helper(int n) { this.n = n; } // ... }
en este ejemplo, helper no va a estar nunca a medio inicializar porque sus atributos son final. Además, es volatile, con lo que todos los hilos verán el valor actualizado (cuando lo esté).
- Es necesario tener en cuenta que la JMM permite hacer visible un objeto antes de haber concluido su inicialización. En la construcción de los objetos, primero se asigna valor por defecto a todos los campos y permite asignar memoria al objeto en el lugar apuntado por la referencia (que ya no es null) antes de ejecutar el constructor, por lo que es posible ver un objeto que existe, con valores por defecto antes de que haya terminado de ejecutarse su constructor.
Para publicar un objeto de forma segura, tanto la referencia al objeto como el estado del objeto deben hacerse visibles a la vez al resto de los hilos.
- Los bloques sincronizados proveen una garantía mas solida que volatile. Las escrituras previa a la escritura de una variable volatile no pueden ser reordenadas a después del volatile pero las lecturas si. Igualmente, las lecturas despues de volatile no pueden ser movidas a antes del volatile pero las escrituras si.
- Ahora sí:
FIN
Conferencia Agile-Spain 2013 – R1 (#cas2k13)
[Voy a acometer este articulo de forma agile, habrá varias entregas incrementales, pero no sobre-escribiré las versiones, dejare las anteriores cuando incremente la actual, para que el que quiera, se quede con la versión reducida de este articulo. Ademas, si no veo visitas, no ampliare el articulo]
Los pasados 10 y 11 de Octubre estuvimos en la CAS de este año y es obligado que haga una reseña a la misma.
Valoración…
Mi valoración, de 1 a 10, es un 8
Lo que me gusto…
- La organización, las increíbles keynotes (y eso que me perdí la de Tobias Mayer) y algunas de las charlas.
- Además, se impuso el «entender el agilismo» al «estas son las reglas a seguir» o mejor dicho, las bases antes de las liturgias (ya sabéis de que charla es esto).
- Me gustó que se cumpliesen los horarios, perfectamente controlado por la organización.
- Hubo un buen reparto entre inicial e intermedio y entre caso de uso (como la genial «the sky way») y charla mas de «cultura».
- El tiempo de las charlas estuvo perfectamente controlado por la organización y eso que en las charlas de 15 minutos era harto complicado.
Para darle un 10…
- Hay que subir el nivel de alguna charla tanto en forma como en fondo para un evento como este
- Recoger mejor el feedback, somos vagos…
- Además, a mi me habría venido bien «obligar» un poco el networking, con algún juego, ejemplo hacer grupos aleatorios (número en la bolsa de bienvenida) y proponer un reto a resolver pero es solo por mi forma de ser, mucha gente no necesita que le «empujen»
En detalle…
Era mi primera CAS y ya os anticipo que ha sido bestial, que me ha encantado y que he vuelto con mucha ilusión, tanto por haber asentado la dirección de algunas cosas que estoy haciendo como por coger ideas nuevas.
Si estáis en este universo paralelo del agilismo, os aseguro que es imprescindible acudir a este tipo de eventos. Como sabemos, no hay receta mágica, no hay un agile handbook para aplicar en los proyectos (si lo tenéis, como bien ha dicho Sander Hoogendoorn en el codemotion, tiradlo a la basura), con lo que las experiencias y opiniones de los demás son el mejor aliado. Y nuestros propios fracasos, claro.
En mi caso tengo identificados los puntos fuertes y flojos que tenemos, las cosas que funcionan y las que no, y me voy con un par de acciones a corto y un buen montón de ideas que tengo que madurar poco a poco, necesito asentar algunas bases antes de avanzar.
La organización de la misma ha sido impecable.
El sitio, increíble. El edificio, el Bizkaia Aretoa, tenia unos ventanales geniales desde los que se podía contemplar el Guggenheim. Las salas, salvo en una charla (de las que fui) tuvieron sitio para las mismas.
La comida el segundo día fue genial, mejorando el primer día. Solo falto poner mas mesas y repartirlas mas. Y el café igual, perfecto. Hasta pusieron un poco de lluvia el segundo día para que tuviésemos un día típico de Bilbao.
El registro, fue perfecto, al menos en mi caso. Empezaba a las 9.00 pero habilitaron un periodo el día previo por la tarde para agilizar en previsión de la gran afluencia esperada (mas de 300 personas, si no recuerdo mal).
Nosotros llegamos en tren a las 21h, con lo que no teníamos opción. No obstante, abrieron (y avisaron) el registro a las 8.30 en vez de a las 9 y, llegando sobre esa hora, nos tardamos 1 minuto en registrarnos.
Y pudimos asistir al evento que llevaba tanto tiempo esperando, desde abril concretamente. En abril pasado hice un curso de Agile Coaching con Jose Ramos Diaz y Ariel Ber, que me dejo con muchas ganas de mas, y decidí que no iba a perderme la CAS de este año…
…y me he venido con muchas cosas, entre ellas: «… primero las bases y después las liturgias» (desde el minuto 01:02:21).
Conferencia Agile-Spain 2013 – R0
[Voy a acometer este articulo de forma agile, habrá varias entregas incrementales, pero no sobre-escribiré las versiones, dejare las anteriores cuando incremente la actual, para que el que quiera, se quede con la versión reducida de este articulo. Ademas, si no veo visitas, no ampliare el articulo]
Mi valoración, de 1 a 10, es un 8
Lo que me gusto:
- La organización, las increíbles keynotes (y eso que me perdí la de Tobias Mayer) y algunas de las charlas
- Además, se impuso el «entender el agilismo» al «estas son las reglas a seguir» o mejor dicho, las bases antes de las liturgias (ya sabéis de que charla es esto).
- Me gustó que se cumpliesen los horarios, perfectamente controlado por la organización.
- Hubo un buen reparto entre inicial e intermedio y entre caso de uso (como la genial «the sky way») y charla mas de «cultura».
Para darle un 10
- Hay que subir el nivel de alguna charla tanto en forma como en fondo para un evento como este
- Recoger mejor el feedback, somos vagos…
- Además, a mi me habría venido bien «obligar» un poco el networking, con algún juego, ejemplo hacer grupos aleatorios (número en la bolsa de bienvenida) y proponer un reto a resolver pero es solo por mi forma de ser, mucha gente no necesita que le «empujen».