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)

KAS pq

[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»

gungenein2013-10-10 18.25.03_pq

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.


2013-10-10 16.28.17-1pqLa 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

KAS pq

[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».

Codemotion 2013

Ayer y hoy se celebraba en Madrid, en el Campus Sur de la Universidad Politécnica, el Codemotion 2013.

Este evento, organizado por primera vez el año pasado en España, este año ampliaba su duración a dos días trayendo a más ponentes y organizando más actividades.

En total han sido algo más de 1500 personas las que hemos asistido este año, superando el número de asistentes del año pasado.

Codemotion Madrid

Charlas, ponentes y actividades.

Más de 90 charlas a lo largo de 2 días con ponentes con experiencia en distintos campos de la Informática, lo que ha obligado a seleccionar aquellas que a priori pudiesen parecer más interesantes o útiles, para la experiencia o intereses de cada uno, por lo que había que renunciar a otras por la dificultad obvia de no poder estar en más de un sitio a la vez.

Así he podido asistir a charlas introductorias a distintos frameworks para desarrollo de aplicaciones para móviles, a otra sobre APIs o al desarrollo de backends en la nube con Windows Azure, donde he podido conocer herramientas muy interesantesa tener en cuenta para el futuro.

Las salas llenas para las charlas

Las salas llenas para las charlas

Entre los ponentes hemos tenido muchos perfiles, nacionales e internacionles, desde programadores a gente experimentada experta en su campo, a los que hay que agradecer desde el inicio su esfuerzo y dedicación para preparar algo para este evento.

Pero no todo eran charlas, y los Sponsor (en su mayoría) habían preparado actividades y meetups con los asistentes, e incluso algún concurso al que cualquiera podía apuntarse.

Además existía la posibilidad, para quien esté buscando cambiar, de dejar su CV personalmente o incluso recibir asesoramiento para hacer más atractivo el CV.

Tan solo se ha echado de menos más presencia nacional de peso, ya que incluso alguna patrocinadora se ha dejado ver poco por el evento.

Mis Impresiones.

En mi caso, que iba totalmente abierto a escuchar, la experiencia ha sido muy satisfactoria.

Es cierto que algunas charlas sabían a poco, porque además de escuchar sobre tal o cuál cosa a uno le entraba el gusanillo de profundizar allí mismo en el tema, o que de alguna podíamos haber esperado algo más.

Pero en general las charlas daban lo que anunciaban, quitando alguna pequeña confusión de menor importancia, y resultaban amenas.

En cuanto a los ponentes, además de conocer el tema del que nos hablaban se ha notado esa falta de don para comunicar que tenemos la inmensa mayoría, aunque también hemos tenido a auténticos showman capaces de engancharte desde el primer momento y llevarte por donde querían, consiguiendo que te quedases con ganas de escucharles un rato más.

Y en lo que respecta a la Organización, aunque ha habido algún fallo por alguna charla cancelada y no avisada previamente, o un poco de caos por falta de información sobre alguna actividad, hay que reconocer el gran esfuerzo que han hecho para que pudiésemos disfrutar del evento.

Clausura

Clausura

En resumen, una muy grata experiencia rodeado de gente que, como nosotros, disfruta creando y desarrollando software. Además en un lugar estupendo, la Escuela Universitaria de Informática, a donde me he alegrado de volver después de tantos años, que también ha puesto mucho de su parte para permitir que se celebrase el evento.

Así que espero que los próximos años podamos seguir disfrutando de más eventos Codemotion en España, y sobre todo que más gente joven empiece a conocerlo y se anime a participar.