Simultaneidad, BlockingQueues (Java 7) - 1

"¡Hola, amigo!"

"¡Hola Kim!"

"Hoy les voy a hablar de la concurrencia".

" La concurrencia es una biblioteca de clases de Java que incluye clases especiales que han sido optimizadas para trabajar desde múltiples subprocesos. Este es un tema muy interesante y extenso. Pero hoy solo vamos a recibir una introducción. El paquete se llama java.util. paquete concurrente. Te hablaré de un par de clases interesantes".

" Tipos atómicos " .

"Ya sabe que incluso count ++ no es una operación segura para subprocesos. Cuando una variable se incrementa en 1, en realidad se realizan tres operaciones. Como resultado, puede haber un conflicto cuando se cambia la variable".

"Sí, Ellie me dijo no hace mucho:"

Hilo 1 Hilo 2 Resultado
register1 = count;
register1++;
count = register1;
register2 = count;
register2++;
count = register2;
register1 = count;
register2 = count;
register2++;
count = register2;
register1++;
count = register1;

"Exactamente. Luego, Java agregó tipos de datos para realizar estas operaciones como una sola, es decir, atómicamente (un átomo es indivisible)".

"Por ejemplo, Java tiene AtomicInteger, AtomicBoolean, AtomicDouble , etc."

"Supongamos que necesitamos hacer una clase «contraria»:"

Ejemplo
class Counter
{
 private int c = 0;

 public void increment()
 {
  c++;
 }

 public void decrement()
 {
  c--;
 }

 public int value()
 {
  return c;
 }
}

"¿Cómo haría que los objetos de esta clase fueran seguros para subprocesos?"

"Bueno, sincronizaría todos los métodos y terminaría con eso:"

Ejemplo
class synchronized Counter
{
 private int c = 0;

 public synchronized void increment()
 {
  c++;
 }

 public synchronized void decrement()
 {
  c--;
 }

 public synchronized int value()
 {
  return c;
 }
}

"Buen trabajo. Pero, ¿cómo se vería si usáramos tipos atómicos:"

Ejemplo
class AtomicCounter
{
 private AtomicInteger c = new AtomicInteger(0);

 public void increment()
 {
  c.incrementAndGet();
 }

 public void decrement()
 {
  c.decrementAndGet();
 }

 public int value()
 {
  return c.get();
 }
}

"Tu clase y mi clase funcionan de la misma manera, pero la clase con un AtomicInteger funciona más rápido".

"Bueno, ¿es una pequeña diferencia?"

"Sí. Basado en mi experiencia, siempre recomiendo liderar con sincronizado. Solo cuando se haya escrito todo el código de la aplicación y haya comenzado el proceso de optimización, debe comenzar a reescribir el código para usar los tipos atómicos. Pero en cualquier caso, quería que usted saber que tales tipos existen. Incluso si no los usa activamente, siempre existe la posibilidad de que se encuentre con el código donde se usan".

"Estoy de acuerdo. Eso tiene sentido".

"Por cierto, ¿te diste cuenta de que los tipos atómicos no son inmutables ? A diferencia de la clase Integer estándar , AtomicInteger contiene métodos para cambiar su estado interno".

"Entendido. Al igual que String y StringBuffer ".

"Si, algo así."

" Colecciones seguras para subprocesos " .

"Como ejemplo de tal colección, puedo presentar ConcurrentHashMap. ¿Cómo haría que HashMap sea seguro para subprocesos?"

"¿Hacer que todos sus métodos estén sincronizados?"

"Claro, pero ahora imagina que tienes uno de esos SynchronizedHashMap y docenas de subprocesos que acceden a él. Y cien veces por segundo se agrega una nueva entrada al mapa y, en el proceso, todo el objeto se bloquea para leer y escribir".

"Bueno, este es el enfoque estándar. ¿Qué puedes hacer?"

"A los creadores de Java se les ocurrieron algunas cosas geniales".

"Primero, almacenan datos en un ConcurrentHashMap en un solo bloque, pero lo dividen en partes llamadas 'depósitos'. Y cuando alguien cambia los datos en un ConcurrentHashMap, solo bloqueamos el depósito al que se accede, en lugar de todo el objeto. En otro palabras, muchos hilos pueden cambiar el objeto simultáneamente".

"Segundo, ¿recuerdas que no puedes iterar sobre los elementos de la lista/mapa y cambiar la lista al mismo tiempo? Dicho código generará una excepción:"

No itere sobre los elementos de una colección en un bucle y cámbielos simultáneamente
HashMap<String, Integer> map = new HashMap<String, Integer>();

for (String key: map.keySet())
{
 if (map.get(key) == 0)
  map.remove(key);
}

"Pero en ConcurrentHashMap, puedes:"

No itere sobre los elementos de una colección en un bucle y cámbielo simultáneamente"
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();

for (String key: map.keySet())
{
 if (map.get(key) == 0)
  map.remove(key);
}

"El paquete concurrente tiene muchas ventajas. Solo necesitamos entender muy bien estas clases para poder usarlas".

"Ya veo. Gracias, Kim. Estas son clases verdaderamente interesantes. Espero que algún día las domine como un virtuoso".