Contadores Atómicos
Cuanto tiempo sin escribir… en fin. Siempre me llamo la atención que en Java muchas de las operaciones sobre primitivos no son atómicas. El statement c++; por ejemplo no es atómico y puede traernos muchos dolores de cabeza en entornos de multithreading. Vale recordar, consideramos una instrucción como “atómica” si no puede ser interrumpida para darle paso a otro thread. En el siguiente ejemplo se presentan el caso de dos threads que consumen concurrentemente un contador. Si ejecutan el código podrán ver que no se obtiene el resultado esperado.
public class Main {
public static void main(String[] args) {
Contador c = new Contador();
new Corredor(c).start();
new Corredor(c).start();
}
}
class Corredor extends Thread {
private Contador contador;
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
System.out.println(contador.incrementAndGet());
Thread.sleep(500);
} catch(Exception e) {
}
}
}
public Corredor(Contador contador) {
this.contador = contador;
}
}
class Contador {
private int value = 0;
public int incrementAndGet() {
return ++value;
}
}
En la pantalla terminamos con algo parecido a
1 2 2 3 3 4 4 5 6 6 7 7 8 8 9 9 10 10 11 ……
De alguna forma estamos forzando un poco esa situacion poniendo a dormir el Thread luego de llamar al metodo incrementAndGet() pero es una situacion que perfectamente se puede dar cuando hay muchos threads en ejecución. Podriamos corregir esta situación synchronizando el metodo incrementAndGet() pero produciría una sobrecarga que quizá no estemos dispuestos a pagar.
java.util.concurrent.atomic.AtomicInteger es la clase que vamos a usar para solucionar este problema. AtomicInteger se comporta como un Integer comun y corriente pero es mutable a traves de metodos atómicos. Lo mejor es que los metodos no son sincronizados de forma tradicional sino por otras oscuras tecnicas que en su momento trataré. Esta clase posee metodos tales como addAndGet(int delta) que incrementa en un delta de forma atomica, decrementAndGet(), getAndAdd(), getAndIncrement() y muchos otros que hacen más o menos lo que indican los nombres pero siempre siempre de forma atómica.
Más información en http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/atomic/AtomicInteger.html