Database
 sql >> Base de données >  >> RDS >> Database

Une introduction aux API de collecte simultanée en Java

Les API de collection simultanées, à l'exception de l'API de collection Java, sont un ensemble d'API de collections conçues et optimisées spécifiquement pour l'accès multithread synchronisé. Ils sont regroupés sous le java.util.concurrent emballer. Cet article fournit une vue d'ensemble et présente son utilisation à l'aide d'un exemple de scénario approprié.

Un aperçu

Java prend en charge le multithreading et la concurrence depuis sa création. Les threads sont créés soit en implémentant le Runnable interface ou en étendant le Thread classe. La synchronisation est réalisée par le mot-clé appelé synchronisation . Java fournit également le mécanisme de communication entre les threads. Ceci est réalisé à l'aide de notify() et attendre() méthodes, qui font partie de l'Object classe. Bien que ces techniques de multithreading innovantes fassent partie de certaines des excellentes fonctionnalités de Java, elles ne suffisent pas à répondre aux besoins d'un programmeur qui a besoin d'une capacité de multithreading intensive prête à l'emploi. En effet, un programme concurrent a besoin de plus que d'être capable de créer des threads et de faire quelques manipulations rudimentaires. Il nécessite de nombreuses fonctionnalités de haut niveau telles que des pools de threads, des gestionnaires d'exécution, des sémaphores, etc.

Cadre de collecte existant

Java dispose déjà d'un cadre de collecte complet. Les collections sont très bonnes dans ce qu'elles font et peuvent également être utilisées dans les applications de thread Java. En outre, il existe un mot-clé, appelé synchronisé , pour les rendre thread-safe. Bien que superficiellement, il puisse sembler qu'ils soient agréables à utiliser en multithreading, la manière dont la sécurité des threads est obtenue est le principal goulot d'étranglement dans son implémentation simultanée. En dehors de la synchronisation explicite, ils ne sont pas conçus sous le paradigme de la mise en œuvre simultanée dès le départ. La synchronisation de ces collections est réalisée en sérialisant tous les accès à l'état de la collection. Cela signifie que, bien que nous puissions avoir une certaine simultanéité, en raison du traitement sérialisé sous-jacent, cela fonctionne sur un principe qui est en fait le contraire. La sérialisation pèse lourdement sur les performances, en particulier lorsque plusieurs threads se disputent le verrou à l'échelle de la collection.

Nouvelles API de collecte

Les API de collecte simultanée sont un ajout à Java à partir de la version 5 et font partie du package appelé java.util.concurrent . Ils constituent une amélioration des API de collecte existantes et ont été conçus pour un accès simultané à partir de plusieurs threads. Par exemple, ConcurrentHashMap est en fait la classe dont nous avons besoin lorsque nous voulons utiliser une Map basée sur le hachage synchronisé la mise en oeuvre. De même, si nous voulons une Liste à dominante traversée et thread-safe , nous pouvons en fait utiliser le CopyOnWriterArrayList classe. Le nouveau ConcurrentMap l'interface fournit un certain nombre d'actions composées sous une seule méthode, comme putIfPresent , computeIfPresent , remplacer , fusionner , etc. Il existe de nombreuses classes de ce type dans la nouvelle structure de collecte simultanée. Pour n'en nommer que quelques-uns :ArrayBlockingQueue , ConcurrentLinkedDeque , ConcurrentLinkedQueue , ConcurrentSkipListMap , ConcurrentSkipListSet , CopyOnWriteArraySet , DelayQueue , LinkedBlockingDeque , File d'attente de blocage liée , File d'attente de transfert liée , File d'attente de blocage prioritaire , File d'attente synchrone , et autres.

Files d'attente

Les types de collection, tels que File d'attente et BlockingQueue , peut être utilisé pour conserver temporairement un élément, en attente de sa récupération en mode FIFO pour traitement. ConcurrentLinkQueue , d'autre part, est une file d'attente FIFO traditionnelle implémentée en tant que file d'attente thread-safe illimitée basée sur des nœuds liés. File d'attente de blocage prioritaire est une file d'attente de blocage illimitée qui utilise les mêmes normes d'ordre que celle de PriorityQueue non simultanée et fournit des opérations de récupération de blocage.

Cartes

Dans les anciennes classes de collection lorsque la synchronisation est appliquée, elle maintient les verrous pendant la durée de chaque opération. Il y a des opérations, comme le get méthode de HashMap ou contient méthode de Liste , qui impliquent des calculs complexes en arrière-plan lorsqu'ils sont invoqués. Par exemple, pour trouver un élément spécifique dans une liste, il invoque automatiquement le égal méthode. Cette méthode nécessite certains calculs pour comparer chaque élément de la liste ; cela peut prendre beaucoup de temps pour terminer la tâche. C'est pire dans une collection basée sur le hachage. Si les éléments des cartes de hachage sont inégalement répartis, parcourir une longue liste et appeler equals peut prendre très longtemps. C'est un problème car cela peut affecter les performances globales de l'application.

Contrairement à HashMap , ConcurrentHashMap utilise une stratégie complètement différente. Au lieu de fournir un verrou commun pour chaque méthode synchronisée, il utilise une technique appelée lock stripping . Il s'agit d'une meilleure solution pour la simultanéité et l'évolutivité. Le décapage des serrures utilise des serrures séparées pour des godets séparés. Par conséquent, la contention de threads est découplée de la structure de données sous-jacente et imposée à la place au bucket. Par exemple, l'implémentation de ConcurrentHashMap utilise un tableau de 16 verrous, dont chacun protège 1/16 des seaux de hachage ; le godet N est gardé par l'écluse N mod 16… cela réduit la demande pour une écluse donnée d'environ un facteur 16. C'est grâce à cette technique que ConcurrentHashMap prend en charge au moins 16 rédacteurs simultanés par défaut et d'autres peuvent être pris en charge sur demande.

CopyOnWriterArrayList

C'est une bonne alternative à la Liste synchronisée et ne vous oblige pas à appliquer un mécanisme de verrouillage pendant l'itération. Les itérateurs conservent une référence au tableau de sauvegarde au début de l'itération et ne la modifient pas. Par conséquent, une brève synchronisation est nécessaire pour obtenir le contenu du tableau. Plusieurs threads peuvent accéder à la collection sans interférer les uns avec les autres. Même les modifications à partir de plusieurs threads ne subissent pas de conflit. Il existe un équivalent défini de cette liste de tableaux, appelé CopyOnWriterSet , qui peut être utilisé pour remplacer Set synchronisé sur le besoin de simultanéité.

Un exemple rapide

Il existe de nombreuses classes dans la collection simultanée. Leur utilisation n'est pas si difficile pour quiconque est familier avec l'ancien cadre de collecte. Par souci d'exhaustivité, voici un exemple pour donner un aperçu de ses utilisations dans la programmation 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.");
         }
      }
   }
}

Conclusion

Le plus grand avantage de l'utilisation des classes de collecte simultanées est peut-être leur évolutivité et leur faible risque. Les API de collecte simultanée de Java fournissent une gamme de classes spécialement conçues pour gérer les opérations simultanées. Ces classes sont des alternatives au Java Collection Framework et fournissent des fonctionnalités similaires, à l'exception de la prise en charge supplémentaire de la concurrence. Par conséquent, la courbe d'apprentissage pour le programmeur qui connaît déjà le Java Collection Framework est presque plate. Les classes sont définies dans le package java.util.concurrent . Ici, j'ai essayé de donner un aperçu pour commencer et utiliser les API de collecte si nécessaire.

Références

  • Documentation de l'API Java
  • Goetz, Brian et Tim Peierls. Concurrence Java en pratique . Pearson, 2013.