sql >> Base de Datos >  >> RDS >> Database

Introducción a las API de recopilación simultánea en Java

Las API de recopilación simultánea, además de la API de recopilación de Java, son un conjunto de API de recopilación que están diseñadas y optimizadas específicamente para el acceso multiproceso sincronizado. Están agrupados bajo java.util.concurrent paquete. Este artículo proporciona una descripción general y presenta su uso mediante el uso de un escenario de ejemplo apropiado.

Una visión general

Java ha soportado subprocesos múltiples y concurrencia desde su inicio. Los subprocesos se crean implementando Runnable interfaz o extendiendo el Thread clase. La sincronización se logra mediante la palabra clave llamada sincronización . Java también proporciona el mecanismo para la comunicación entre los hilos. Esto se logra con la ayuda de notify() y esperar() métodos, que son parte del Objeto clase. Aunque estas técnicas innovadoras de subprocesos múltiples son parte de algunas de las excelentes características de Java, se quedan un poco cortas a la hora de satisfacer las necesidades de un programador que requiere una capacidad intensiva de subprocesos múltiples lista para usar. Esto se debe a que un programa concurrente necesita algo más que poder crear subprocesos y realizar algunas manipulaciones rudimentarias. Requiere muchas funciones de alto nivel, como grupos de subprocesos, administradores de ejecución, semáforos, etc.

Marco de colección existente

Java ya tiene un marco de colección completo. Las colecciones son muy buenas en lo que hacen y también se pueden usar en aplicaciones de subprocesos de Java. Además, hay una palabra clave, llamada synchronized , para que sean seguros para subprocesos. Aunque superficialmente puede parecer que son buenos para ser utilizados en subprocesos múltiples, la forma en que se logra la seguridad de los subprocesos es el principal cuello de botella en su implementación concurrente. Aparte de la sincronización explícita, no están diseñados bajo el paradigma de implementación concurrente desde el principio. La sincronización de estas colecciones se logra serializando todos los accesos al estado de la colección. Esto significa que, aunque podemos tener cierta concurrencia, debido al procesamiento serializado subyacente, funciona según un principio que en realidad es el opuesto. La serialización afecta mucho el rendimiento, especialmente cuando varios subprocesos compiten por el bloqueo de toda la colección.

Nuevas API de colección

Las API de recopilación simultánea son una adición a Java desde la versión 5 y forman parte del paquete llamado java.util.concurrent . Son una mejora de las API de recopilación existentes y se han diseñado para el acceso simultáneo desde varios subprocesos. Por ejemplo, ConcurrentHashMap es en realidad la clase que necesitamos cuando queremos usar un mapa basado en hash sincronizado implementación. Del mismo modo, si queremos una List dominante en el recorrido y segura para subprocesos , en realidad podemos usar CopyOnWriterArrayList clase. El nuevo ConcurrentMap La interfaz proporciona una serie de acciones compuestas bajo un solo método, como putIfPresent , computeIfPresent , reemplazar , combinar , y así. Hay muchas clases de este tipo que se encuentran dentro del nuevo marco de recopilación concurrente. Por nombrar algunos:ArrayBlockingQueue , Deque vinculado concurrente , Cola enlazada simultánea , Mapa de lista de saltos simultáneos , Conjunto de listas de saltos simultáneos , Copiar en conjunto de matriz de escritura , Cola de retraso , Deque de bloqueo vinculado , Cola de bloqueo enlazada , Cola de transferencia vinculada , Cola de bloqueo de prioridad , Cola sincrónica y otros.

Colas

Los tipos de colección, como Queue y BlockingQueue , se puede utilizar para retener un elemento temporalmente, en espera de su recuperación en modo FIFO para su procesamiento. Cola de enlaces simultáneos , por otro lado, es una cola FIFO tradicional implementada como una cola ilimitada y segura para subprocesos basada en nodos vinculados. Cola de bloqueo de prioridad es una cola de bloqueo ilimitada que utiliza las mismas normas de ordenación que las PriorityQueue no concurrentes y suministros que bloquean las operaciones de recuperación.

Mapas

En las clases de colección más antiguas, cuando se aplica la sincronización, mantiene los bloqueos durante la duración de cada operación. Hay operaciones, como get método de HashMap o contiene método de Lista , que involucran cálculos complejos detrás de escena cuando se invocan. Por ejemplo, para encontrar un elemento específico en una lista, invoca automáticamente el igual método. Este método requiere ciertos cálculos para comparar cada elemento de la lista; puede llevar mucho tiempo completar la tarea. Esto es peor en una colección basada en hash. Si los elementos en los mapas hash están distribuidos de manera desigual, recorrer una lista larga y llamar a equals puede llevar mucho tiempo. Esto es un problema porque puede afectar el rendimiento general de la aplicación.

A diferencia de HashMap , Mapa de hash concurrente usa una estrategia completamente diferente. En lugar de proporcionar un bloqueo común para cada método sincronizado, utiliza una técnica llamada eliminación de bloqueos . Esta es una mejor solución tanto para la concurrencia como para la escalabilidad. El desmontaje de cerraduras utiliza cerraduras separadas para cubos separados. Como resultado, la contención de subprocesos se desacopla de la estructura de datos subyacente y, en su lugar, se impone en el depósito. Por ejemplo, la implementación de ConcurrentHashMap utiliza una matriz de 16 bloqueos, cada uno de los cuales protege 1/16 de los cubos hash; el cubo N está protegido por la cerradura N mod 16... esto reduce la demanda de cualquier cerradura en aproximadamente un factor de 16. Es debido a esta técnica que ConcurrentHashMap admite al menos 16 escritores simultáneos de manera predeterminada y se pueden acomodar más a pedido.

Copiar en la lista de matrices de escritura

Es una buena alternativa a la Lista sincronizada y no requiere que aplique un mecanismo de bloqueo durante la iteración. Los iteradores conservan una referencia a la matriz de respaldo al comienzo de la iteración y no la cambian. Por lo tanto, requiere una breve sincronización para obtener el contenido de la matriz. Múltiples subprocesos pueden acceder a la colección sin interferir entre sí. Incluso la modificación de múltiples subprocesos no sufre contención. Hay una contraparte establecida de esta lista de arreglos, llamada CopyOnWriterSet , que se puede usar para reemplazar el Set sincronizado en necesidad de concurrencia.

Un ejemplo rápido

Hay muchas clases en la colección concurrente. Su uso no es tan difícil para cualquiera que esté familiarizado con el marco de colección anterior. En aras de la exhaustividad, aquí hay un ejemplo para dar una idea de sus usos en la programación de Java.

package org.mano.example;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ProducerConsumerDemo {
   static BlockingQueue<Integer> queue = new
      LinkedBlockingQueue<>(5);
   public static void main(String[] args) throws
         InterruptedException {
      int noOfProducers = 7;
      int noOfConsumers = 9;
      for (inti = 0; i < noOfProducers; i++) {
         new Thread(new Producer(), "PRODUCER").start();
      }
      for (int i = 0; i < noOfConsumers; i++) {
         new Thread(new Consumer(), "CONSUMER").start();
      }
      System.exit(0);
   }
   static class Producer implements Runnable {
      Random random = new Random();
      public void run() {
         try {
            int num = random.nextInt(100);
            queue.put(num);
            System.out.println("Produced: " + num
               + " Queue size : "+ queue.size());
            Thread.sleep(100);
         } catch (InterruptedException ex) {
            System.out.println("Producer is interrupted.");
         }
      }
   }
   static class Consumer implements Runnable {
      public void run() {
         try {
            System.out.println("Consumed: " + queue.take()
               + " Queue size : "+ queue.size());
            Thread.sleep(100);
         } catch (InterruptedException ex) {
            System.out.println("Consumer is interrupted.");
         }
      }
   }
}

Conclusión

Quizás el mayor beneficio de usar las clases de colección concurrentes es su escalabilidad y bajo riesgo. Las API de recopilación concurrente de Java proporcionan una variedad de clases que están diseñadas específicamente para manejar operaciones concurrentes. Estas clases son alternativas a Java Collection Framework y brindan una funcionalidad similar excepto con el soporte adicional de concurrencia. Por lo tanto, la curva de aprendizaje para el programador que ya conoce Java Collection Framework es casi plana. Las clases se definen en el paquete java.util.concurrent . Aquí, he tratado de brindar una descripción general para comenzar y usar las API de recopilación cuando sea necesario.

Referencias

  • Documentación de la API de Java
  • Goetz, Brian y Tim Peierls. Simultaneidad de Java en la práctica . Pearson, 2013.