Concurrence Java - Guide rapide

Java est un langage de programmation multi-thread qui signifie que nous pouvons développer un programme multi-thread en utilisant Java. Un programme multithread contient deux ou plusieurs parties qui peuvent s'exécuter simultanément et chaque partie peut gérer une tâche différente en même temps en utilisant de manière optimale les ressources disponibles, en particulier lorsque votre ordinateur dispose de plusieurs processeurs.

Par définition, le multitâche se produit lorsque plusieurs processus partagent des ressources de traitement communes telles qu'un processeur. Le multi-threading étend l'idée du multitâche dans des applications où vous pouvez subdiviser des opérations spécifiques au sein d'une seule application en threads individuels. Chacun des threads peut s'exécuter en parallèle. Le système d'exploitation divise le temps de traitement non seulement entre différentes applications, mais également entre chaque thread d'une application.

Le multi-threading vous permet d'écrire de manière à ce que plusieurs activités puissent se dérouler simultanément dans le même programme.

Cycle de vie d'un fil

Un fil passe par différentes étapes de son cycle de vie. Par exemple, un thread est né, démarré, s'exécute, puis meurt. Le diagramme suivant montre le cycle de vie complet d'un thread.

Voici les étapes du cycle de vie -

  • New- Un nouveau thread commence son cycle de vie dans le nouvel état. Il reste dans cet état jusqu'à ce que le programme démarre le thread. Il est également appelé unborn thread.

  • Runnable- Après le démarrage d'un thread nouvellement né, le thread devient exécutable. Un thread dans cet état est considéré comme exécutant sa tâche.

  • Waiting- Parfois, un thread passe à l'état d'attente pendant que le thread attend qu'un autre thread exécute une tâche. Un thread revient à l'état exécutable uniquement lorsqu'un autre thread signale au thread en attente de continuer à s'exécuter.

  • Timed Waiting- Un thread exécutable peut entrer dans l'état d'attente chronométré pendant un intervalle de temps spécifié. Un thread dans cet état revient à l'état exécutable lorsque cet intervalle de temps expire ou lorsque l'événement qu'il attend se produit.

  • Terminated (Dead) - Un thread exécutable entre dans l'état terminé lorsqu'il termine sa tâche ou s'arrête autrement.

Priorités des threads

Chaque thread Java a une priorité qui aide le système d'exploitation à déterminer l'ordre dans lequel les threads sont planifiés.

Les priorités des threads Java sont comprises entre MIN_PRIORITY (une constante de 1) et MAX_PRIORITY (une constante de 10). Par défaut, chaque thread a la priorité NORM_PRIORITY (une constante de 5).

Les threads avec une priorité plus élevée sont plus importants pour un programme et doivent se voir allouer du temps processeur avant les threads de priorité inférieure. Cependant, les priorités des threads ne peuvent pas garantir l'ordre dans lequel les threads s'exécutent et dépendent beaucoup de la plate-forme.

Créer un thread en implémentant une interface exécutable

Si votre classe est destinée à être exécutée en tant que thread, vous pouvez y parvenir en implémentant un Runnableinterface. Vous devrez suivre trois étapes de base -

Étape 1

Dans un premier temps, vous devez implémenter une méthode run () fournie par un Runnableinterface. Cette méthode fournit un point d'entrée pour le thread et vous placerez votre logique métier complète dans cette méthode. Voici une syntaxe simple de la méthode run () -

public void run( )

Étape 2

Dans un deuxième temps, vous instanciez un Thread objet utilisant le constructeur suivant -

Thread(Runnable threadObj, String threadName);

Où, threadObj est une instance d'une classe qui implémente leRunnable interface et threadName est le nom donné au nouveau thread.

Étape 3

Une fois qu'un objet Thread est créé, vous pouvez le démarrer en appelant start()méthode, qui exécute un appel à la méthode run (). Voici une syntaxe simple de la méthode start () -

void start();

Example

Voici un exemple qui crée un nouveau thread et commence à l'exécuter -

class RunnableDemo implements Runnable {
   private Thread t;
   private String threadName;

   RunnableDemo(String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      
      try {
      
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      RunnableDemo R1 = new RunnableDemo("Thread-1");
      R1.start();
      
      RunnableDemo R2 = new RunnableDemo("Thread-2");
      R2.start();
   }   
}

Cela produira le résultat suivant -

Output

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

Créer un thread en étendant une classe de thread

La deuxième façon de créer un thread est de créer une nouvelle classe qui étend Threadclasse en utilisant les deux étapes simples suivantes. Cette approche offre plus de flexibilité dans la gestion de plusieurs threads créés à l'aide des méthodes disponibles dans la classe Thread.

Étape 1

Vous devrez remplacer run( )méthode disponible dans la classe Thread. Cette méthode fournit un point d'entrée pour le thread et vous placerez votre logique métier complète dans cette méthode. Voici une syntaxe simple de la méthode run () -

public void run( )

Étape 2

Une fois l'objet Thread créé, vous pouvez le démarrer en appelant start()méthode, qui exécute un appel à la méthode run (). Voici une syntaxe simple de la méthode start () -

void start( );

Example

Voici le programme précédent réécrit pour étendre le Thread -

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   
   ThreadDemo(String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      
      try {

         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      ThreadDemo T1 = new ThreadDemo("Thread-1");
      T1.start();
      
      ThreadDemo T2 = new ThreadDemo("Thread-2");
      T2.start();
   }   
}

Cela produira le résultat suivant -

Output

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

Dans ce chapitre, nous discuterons des différents aspects de la configuration d'un environnement convivial pour Java.

Configuration de l'environnement local

Si vous souhaitez toujours configurer votre environnement pour le langage de programmation Java, cette section vous explique comment télécharger et configurer Java sur votre machine. Voici les étapes de configuration de l'environnement.

Java SE est disponible gratuitement à partir du lien Télécharger Java . Vous pouvez télécharger une version basée sur votre système d'exploitation.

Suivez les instructions pour télécharger Java et exécuter le .exepour installer Java sur votre machine. Une fois que vous avez installé Java sur votre machine, vous devrez définir des variables d'environnement pour qu'elles pointent vers les répertoires d'installation corrects -

Configuration du chemin pour Windows

En supposant que vous ayez installé Java dans le répertoire c: \ Program Files \ java \ jdk -

  • Cliquez avec le bouton droit sur «Poste de travail» et sélectionnez «Propriétés».

  • Cliquez sur le bouton "Variables d'environnement" sous l'onglet "Avancé".

  • Maintenant, modifiez la variable «Path» afin qu'elle contienne également le chemin vers l'exécutable Java. Exemple, si le chemin est actuellement défini sur «C: \ WINDOWS \ SYSTEM32», modifiez votre chemin pour lire «C: \ WINDOWS \ SYSTEM32; c: \ Program Files \ java \ jdk \ bin».

Configuration du chemin pour Linux, UNIX, Solaris, FreeBSD

La variable d'environnement PATH doit être définie pour pointer vers l'endroit où les binaires Java ont été installés. Reportez-vous à la documentation de votre shell, si vous rencontrez des problèmes.

Exemple, si vous utilisez bash comme shell, vous ajouteriez la ligne suivante à la fin de votre '.bashrc: export PATH = / path / to / java: $ PATH'

Éditeurs Java populaires

Pour écrire vos programmes Java, vous aurez besoin d'un éditeur de texte. Il existe des IDE encore plus sophistiqués disponibles sur le marché. Mais pour l'instant, vous pouvez envisager l'un des éléments suivants -

  • Notepad - Sur une machine Windows, vous pouvez utiliser n'importe quel éditeur de texte simple comme Notepad (recommandé pour ce tutoriel), TextPad.

  • Netbeans - Un IDE Java open-source et gratuit qui peut être téléchargé depuis https://netbeans.org/index.html.

  • Eclipse - Un IDE Java développé par la communauté open-source eclipse et peut être téléchargé depuis https://www.eclipse.org/.

Core Java offre un contrôle complet sur le programme multithread. Vous pouvez développer un programme multithread qui peut être suspendu, repris ou complètement arrêté en fonction de vos besoins. Il existe différentes méthodes statiques que vous pouvez utiliser sur les objets thread pour contrôler leur comportement. Le tableau suivant répertorie ces méthodes -

Sr.No. Méthode et description
1

public void suspend()

Cette méthode met un thread dans l'état suspendu et peut être repris à l'aide de la méthode resume ().

2

public void stop()

Cette méthode arrête complètement un thread.

3

public void resume()

Cette méthode reprend un thread, qui a été suspendu à l'aide de la méthode suspend ().

4

public void wait()

Provoque l'attente du thread actuel jusqu'à ce qu'un autre thread appelle la notification ().

5

public void notify()

Réveille un thread unique en attente sur le moniteur de cet objet.

Sachez que les dernières versions de Java ont déconseillé l'utilisation des méthodes suspend (), resume () et stop () et que vous devez donc utiliser les alternatives disponibles.

Exemple

class RunnableDemo implements Runnable {
   public Thread t;
   private String threadName;
   boolean suspended = false;

   RunnableDemo(String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );

      try {
         
         for(int i = 10; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);

            // Let the thread sleep for a while.
            Thread.sleep(300);

            synchronized(this) {
               
               while(suspended) {
                  wait();
               }
            }
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
   
   void suspend() {
      suspended = true;
   }
   
   synchronized void resume() {
      suspended = false;
      notify();
   }
}

public class TestThread {

   public static void main(String args[]) {
      RunnableDemo R1 = new RunnableDemo("Thread-1");
      R1.start();

      RunnableDemo R2 = new RunnableDemo("Thread-2");
      R2.start();

      try {
         Thread.sleep(1000);
         R1.suspend();
         System.out.println("Suspending First Thread");
         Thread.sleep(1000);
         R1.resume();
         System.out.println("Resuming First Thread");
         
         R2.suspend();
         System.out.println("Suspending thread Two");
         Thread.sleep(1000);
         R2.resume();
         System.out.println("Resuming thread Two");
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      } try {
         System.out.println("Waiting for threads to finish.");
         R1.t.join();
         R2.t.join();
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      }
      System.out.println("Main thread exiting.");
   }
}

Le programme ci-dessus produit la sortie suivante -

Production

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 10
Running Thread-2
Thread: Thread-2, 10
Thread: Thread-1, 9
Thread: Thread-2, 9
Thread: Thread-1, 8
Thread: Thread-2, 8
Thread: Thread-1, 7
Thread: Thread-2, 7
Suspending First Thread
Thread: Thread-2, 6
Thread: Thread-2, 5
Thread: Thread-2, 4
Resuming First Thread
Suspending thread Two
Thread: Thread-1, 6
Thread: Thread-1, 5
Thread: Thread-1, 4
Thread: Thread-1, 3
Resuming thread Two
Thread: Thread-2, 3
Waiting for threads to finish.
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Main thread exiting.

Si vous connaissez la communication interprocessus, il vous sera alors facile de comprendre la communication inter-processus. La communication entre threads est importante lorsque vous développez une application dans laquelle deux threads ou plus échangent des informations.

Il existe trois méthodes simples et une petite astuce qui rend la communication de thread possible. Les trois méthodes sont répertoriées ci-dessous -

Sr.No. Méthode et description
1

public void wait()

Provoque l'attente du thread actuel jusqu'à ce qu'un autre thread appelle la notification ().

2

public void notify()

Réveille un thread unique en attente sur le moniteur de cet objet.

3

public void notifyAll()

Réveille tous les threads qui ont appelé wait () sur le même objet.

Ces méthodes ont été implémentées comme finalméthodes dans Object, elles sont donc disponibles dans toutes les classes. Les trois méthodes ne peuvent être appelées qu'à partir d'unsynchronized le contexte.

Exemple

Cet exemple montre comment deux threads peuvent communiquer en utilisant wait() et notify()méthode. Vous pouvez créer un système complexe en utilisant le même concept.

class Chat {
   boolean flag = false;

   public synchronized void Question(String msg) {

      if (flag) {
         
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      System.out.println(msg);
      flag = true;
      notify();
   }

   public synchronized void Answer(String msg) {

      if (!flag) {
         
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      System.out.println(msg);
      flag = false;
      notify();
   }
}

class T1 implements Runnable {
   Chat m;
   String[] s1 = { "Hi", "How are you ?", "I am also doing fine!" };

   public T1(Chat m1) {
      this.m = m1;
      new Thread(this, "Question").start();
   }

   public void run() {
   
      for (int i = 0; i < s1.length; i++) {
         m.Question(s1[i]);
      }
   }
}

class T2 implements Runnable {
   Chat m;
   String[] s2 = { "Hi", "I am good, what about you?", "Great!" };

   public T2(Chat m2) {
      this.m = m2;
      new Thread(this, "Answer").start();
   }

   public void run() {

      for (int i = 0; i < s2.length; i++) {
         m.Answer(s2[i]);
      }
   }
}

public class TestThread {

   public static void main(String[] args) {
      Chat m = new Chat();
      new T1(m);
      new T2(m);
   }
}

Lorsque le programme ci-dessus est respecté et exécuté, il produit le résultat suivant -

Production

Hi
Hi
How are you ?
I am good, what about you?
I am also doing fine!
Great!

L'exemple ci-dessus a été pris puis modifié à partir de [https://stackoverflow.com/questions/2170520/inter-thread-communication-in-java]

Exemple de multithreading avec synchronisation

Voici le même exemple qui imprime la valeur du compteur en séquence et chaque fois que nous l'exécutons, il produit le même résultat.

Exemple

class PrintDemo {
   
   public void printCount() {
      
      try {
         
         for(int i = 5; i > 0; i--) {
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo  PD;

   ThreadDemo(String name,  PrintDemo pd) {
      threadName = name;
      PD = pd;
   }
   
   public void run() {
      
      synchronized(PD) {
         PD.printCount();
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo T1 = new ThreadDemo("Thread - 1 ", PD);
      ThreadDemo T2 = new ThreadDemo("Thread - 2 ", PD);

      T1.start();
      T2.start();

      // wait for threads to end
      try {
         T1.join();
         T2.join();
      } catch (Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Cela produit le même résultat à chaque fois que vous exécutez ce programme -

Production

Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 1  exiting.
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.

Le blocage décrit une situation dans laquelle deux ou plusieurs threads sont bloqués pour toujours, en attente l'un de l'autre. Un blocage se produit lorsque plusieurs threads ont besoin des mêmes verrous mais les obtiennent dans un ordre différent. Un programme Java multithread peut souffrir de la condition de blocage car lesynchronizedLe mot-clé provoque le blocage du thread en cours d'exécution en attendant le verrou, ou le moniteur, associé à l'objet spécifié. Voici un exemple.

Exemple

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
   
      public void run() {
      
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");

            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }

   private static class ThreadDemo2 extends Thread {
   
      public void run() {
      
         synchronized (Lock2) {
            System.out.println("Thread 2: Holding lock 2...");
            
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 1...");
            
            synchronized (Lock1) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

Lorsque vous compilez et exécutez le programme ci-dessus, vous trouvez une situation de blocage et voici la sortie produite par le programme -

Production

Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...

Le programme ci-dessus se bloquera pour toujours car aucun des threads en position de continuer et n'attend l'un l'autre pour libérer le verrou, vous pouvez donc sortir du programme en appuyant sur CTRL + C.

Exemple de solution de blocage

Changeons l'ordre du verrou et exécutons le même programme pour voir si les deux threads attendent toujours l'un l'autre -

Exemple

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
   
      public void run() {
         
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }

   private static class ThreadDemo2 extends Thread {
      
      public void run() {
         
         synchronized (Lock1) {
            System.out.println("Thread 2: Holding lock 1...");
           
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

Donc, le simple fait de changer l'ordre des verrous empêche le programme d'entrer dans une situation de blocage et se termine avec le résultat suivant -

Production

Thread 1: Holding lock 1...
Thread 1: Waiting for lock 2...
Thread 1: Holding lock 1 & 2...
Thread 2: Holding lock 1...
Thread 2: Waiting for lock 2...
Thread 2: Holding lock 1 & 2...

L'exemple ci-dessus vise simplement à clarifier le concept, cependant, il s'agit d'un concept complexe et vous devez vous y plonger en profondeur avant de développer vos applications pour faire face à des situations de blocage.

La classe ThreadLocal est utilisée pour créer des variables locales de thread qui ne peuvent être lues et écrites que par le même thread. Par exemple, si deux threads accèdent à du code faisant référence à la même variable threadLocal, chaque thread ne verra aucune modification de la variable threadLocal effectuée par un autre thread.

Méthodes ThreadLocal

Voici la liste des méthodes importantes disponibles dans la classe ThreadLocal.

Sr.No. Méthode et description
1

public T get()

Renvoie la valeur de la copie du thread actuel de cette variable locale du thread.

2

protected T initialValue()

Renvoie la "valeur initiale" du thread actuel pour cette variable locale du thread.

3

public void remove()

Supprime la valeur du thread actuel pour cette variable locale du thread.

4

public void set(T value)

Définit la copie du thread actuel de cette variable locale du thread sur la valeur spécifiée.

Exemple

Le programme TestThread suivant illustre certaines de ces méthodes de la classe ThreadLocal. Ici, nous avons utilisé deux variables de compteur, l'une est la variable normale et l'autre est ThreadLocal.

class RunnableDemo implements Runnable {
   int counter;
   ThreadLocal<Integer> threadLocalCounter = new ThreadLocal<Integer>();

   public void run() {     
      counter++;

      if(threadLocalCounter.get() != null) {
         threadLocalCounter.set(threadLocalCounter.get().intValue() + 1);
      } else {
         threadLocalCounter.set(0);
      }
      System.out.println("Counter: " + counter);
      System.out.println("threadLocalCounter: " + threadLocalCounter.get());
   }
}

public class TestThread {

   public static void main(String args[]) {
      RunnableDemo commonInstance = new RunnableDemo();

      Thread t1 = new Thread(commonInstance);
      Thread t2 = new Thread(commonInstance);
      Thread t3 = new Thread(commonInstance);
      Thread t4 = new Thread(commonInstance);

      t1.start();
      t2.start();
      t3.start();
      t4.start();

      // wait for threads to end
      try {
         t1.join();
         t2.join();
         t3.join();
         t4.join();
      } catch (Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Cela produira le résultat suivant.

Production

Counter: 1
threadLocalCounter: 0
Counter: 2
threadLocalCounter: 0
Counter: 3
threadLocalCounter: 0
Counter: 4
threadLocalCounter: 0

Vous pouvez voir que la valeur de counter est augmentée par chaque thread, mais threadLocalCounter reste 0 pour chaque thread.

Un java.util.concurrent.ThreadLocalRandom est une classe utilitaire introduite à partir de jdk 1.7 et est utile lorsque plusieurs threads ou ForkJoinTasks sont nécessaires pour générer des nombres aléatoires. Elle améliore les performances et a moins de conflits que la méthode Math.random ().

Méthodes ThreadLocalRandom

Voici la liste des méthodes importantes disponibles dans la classe ThreadLocalRandom.

Sr.No. Méthode et description
1

public static ThreadLocalRandom current()

Renvoie le ThreadLocalRandom du thread actuel.

2

protected int next(int bits)

Génère le prochain nombre pseudo-aléatoire.

3

public double nextDouble(double n)

Renvoie une valeur double pseudo-aléatoire uniformément distribuée entre 0 (inclus) et la valeur spécifiée (exclusive).

4

public double nextDouble(double least, double bound)

Renvoie une valeur pseudo-aléatoire, uniformément distribuée entre la valeur la plus petite donnée (incluse) et la borne (exclusive).

5

public int nextInt(int least, int bound)

Renvoie une valeur pseudo-aléatoire, uniformément distribuée entre la valeur la plus petite donnée (incluse) et la borne (exclusive).

6

public long nextLong(long n)

Renvoie une valeur pseudo-aléatoire distribuée uniformément entre 0 (inclus) et la valeur spécifiée (exclusive).

sept

public long nextLong(long least, long bound)

Renvoie une valeur pseudo-aléatoire, uniformément distribuée entre la valeur la plus petite donnée (incluse) et la borne (exclusive).

8

public void setSeed(long seed)

Lève une exception UnsupportedOperationException.

Exemple

Le programme TestThread suivant illustre certaines de ces méthodes de l'interface de verrouillage. Ici, nous avons utilisé lock () pour acquérir le verrou et unlock () pour libérer le verrou.

import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ThreadLocalRandom;

public class TestThread {
  
   public static void main(final String[] arguments) {
      System.out.println("Random Integer: " + new Random().nextInt());  
      System.out.println("Seeded Random Integer: " + new Random(15).nextInt());  
      System.out.println(
         "Thread Local Random Integer: " + ThreadLocalRandom.current().nextInt());
      
      final ThreadLocalRandom random = ThreadLocalRandom.current();  
      random.setSeed(15); //exception will come as seeding is not allowed in ThreadLocalRandom.
      System.out.println("Seeded Thread Local Random Integer: " + random.nextInt());  
   }
}

Cela produira le résultat suivant.

Production

Random Integer: 1566889198
Seeded Random Integer: -1159716814
Thread Local Random Integer: 358693993
Exception in thread "main" java.lang.UnsupportedOperationException
        at java.util.concurrent.ThreadLocalRandom.setSeed(Unknown Source)
        at TestThread.main(TestThread.java:21)

Ici, nous avons utilisé les classes ThreadLocalRandom et Random pour obtenir des nombres aléatoires.

Une interface java.util.concurrent.locks.Lock est utilisée comme mécanisme de synchronisation des threads similaire aux blocs synchronisés. Le nouveau mécanisme de verrouillage est plus flexible et offre plus d'options qu'un bloc synchronisé. Les principales différences entre un verrou et un bloc synchronisé sont les suivantes:

  • Guarantee of sequence- Le bloc synchronisé ne fournit aucune garantie de la séquence dans laquelle le thread en attente aura accès. L'interface de verrouillage le gère.

  • No timeout- Le bloc synchronisé n'a pas d'option de temporisation si le verrouillage n'est pas accordé. L'interface de verrouillage offre une telle option.

  • Single method - Le bloc synchronisé doit être entièrement contenu dans une seule méthode alors que les méthodes lock () et unlock () d'une interface de verrouillage peuvent être appelées dans différentes méthodes.

Verrouiller les méthodes

Voici la liste des méthodes importantes disponibles dans la classe Lock.

Sr.No. Méthode et description
1

public void lock()

Acquiert la serrure.

2

public void lockInterruptibly()

Acquiert le verrou sauf si le thread en cours est interrompu.

3

public Condition newCondition()

Renvoie une nouvelle instance de Condition liée à cette instance de Lock.

4

public boolean tryLock()

Acquiert le verrou uniquement s'il est libre au moment de l'appel.

5

public boolean tryLock()

Acquiert le verrou uniquement s'il est libre au moment de l'appel.

6

public boolean tryLock(long time, TimeUnit unit)

Acquiert le verrou s'il est libre dans le temps d'attente donné et que le thread en cours n'a pas été interrompu.

sept

public void unlock()

Libère le verrou.

Exemple

Le programme TestThread suivant illustre certaines de ces méthodes de l'interface de verrouillage. Ici, nous avons utilisé lock () pour acquérir le verrou et unlock () pour libérer le verrou.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class PrintDemo {
   private final Lock queueLock = new ReentrantLock();

   public void print() {
      queueLock.lock();

      try {
         Long duration = (long) (Math.random() * 10000);
         System.out.println(Thread.currentThread().getName() 
            + "  Time Taken " + (duration / 1000) + " seconds.");
         Thread.sleep(duration);
      } catch (InterruptedException e) {
         e.printStackTrace();
      } finally {
         System.out.printf(
            "%s printed the document successfully.\n", Thread.currentThread().getName());
         queueLock.unlock();
      }
   }
}

class ThreadDemo extends Thread {
   PrintDemo  printDemo;

   ThreadDemo(String name,  PrintDemo printDemo) {
      super(name);
      this.printDemo = printDemo;
   }   

   @Override
   public void run() {
      System.out.printf(
         "%s starts printing a document\n", Thread.currentThread().getName());
      printDemo.print();
   }
}

public class TestThread {

   public static void main(String args[]) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo t1 = new ThreadDemo("Thread - 1 ", PD);
      ThreadDemo t2 = new ThreadDemo("Thread - 2 ", PD);
      ThreadDemo t3 = new ThreadDemo("Thread - 3 ", PD);
      ThreadDemo t4 = new ThreadDemo("Thread - 4 ", PD);

      t1.start();
      t2.start();
      t3.start();
      t4.start();
   }
}

Cela produira le résultat suivant.

Production

Thread - 1  starts printing a document
Thread - 4  starts printing a document
Thread - 3  starts printing a document
Thread - 2  starts printing a document
Thread - 1   Time Taken 4 seconds.
Thread - 1  printed the document successfully.
Thread - 4   Time Taken 3 seconds.
Thread - 4  printed the document successfully.
Thread - 3   Time Taken 5 seconds.
Thread - 3  printed the document successfully.
Thread - 2   Time Taken 4 seconds.
Thread - 2  printed the document successfully.

Nous avons utilisé la classe ReentrantLock comme implémentation de l'interface Lock ici. La classe ReentrantLock permet à un thread de verrouiller une méthode même s'il a déjà le verrou sur une autre méthode.

Une interface java.util.concurrent.locks.ReadWriteLock permet à plusieurs threads de lire à la fois, mais un seul thread peut écrire à la fois.

  • Read Lock - Si aucun thread n'a verrouillé le ReadWriteLock pour l'écriture, plusieurs threads peuvent accéder au verrou de lecture.

  • Write Lock - Si aucun thread ne lit ou n'écrit, un thread peut accéder au verrou d'écriture.

Verrouiller les méthodes

Voici la liste des méthodes importantes disponibles dans la classe Lock.

Sr.No. Méthode et description
1

public Lock readLock()

Renvoie le verrou utilisé pour la lecture.

2

public Lock writeLock()

Renvoie le verrou utilisé pour l'écriture.

Exemple

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class TestThread {
   private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
   private static String message = "a";

   public static void main(String[] args) throws InterruptedException {
      Thread t1 = new Thread(new WriterA());
      t1.setName("Writer A");
      
      Thread t2 = new Thread(new WriterB());
      t2.setName("Writer B");
      
      Thread t3 = new Thread(new Reader());
      t3.setName("Reader");
      t1.start();
      t2.start();
      t3.start();
      t1.join();
      t2.join();
      t3.join();
   }

   static class Reader implements Runnable {

      public void run() {
         
         if(lock.isWriteLocked()) {
            System.out.println("Write Lock Present.");
         }
         lock.readLock().lock();

         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            System.out.println(Thread.currentThread().getName() +": "+ message );
            lock.readLock().unlock();
         }
      }
   }

   static class WriterA implements Runnable {

      public void run() {
         lock.writeLock().lock();
         
         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            message = message.concat("a");
            lock.writeLock().unlock();
         }
      }
   }

   static class WriterB implements Runnable {

      public void run() {
         lock.writeLock().lock();
         
         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            message = message.concat("b");
            lock.writeLock().unlock();
         }
      }
   }
}

Cela produira le résultat suivant.

Production

Writer A  Time Taken 6 seconds.
Write Lock Present.
Writer B  Time Taken 2 seconds.
Reader  Time Taken 0 seconds.
Reader: aab

Une interface java.util.concurrent.locks.Condition fournit à un thread la capacité de suspendre son exécution, jusqu'à ce que la condition donnée soit vraie. Un objet Condition est nécessairement lié à un Lock et doit être obtenu à l'aide de la méthode newCondition ().

Méthodes de condition

Voici la liste des méthodes importantes disponibles dans la classe Condition.

Sr.No. Méthode et description
1

public void await()

Fait attendre le thread actuel jusqu'à ce qu'il soit signalé ou interrompu.

2

public boolean await(long time, TimeUnit unit)

Fait attendre le thread actuel jusqu'à ce qu'il soit signalé ou interrompu, ou que le temps d'attente spécifié s'écoule.

3

public long awaitNanos(long nanosTimeout)

Fait attendre le thread actuel jusqu'à ce qu'il soit signalé ou interrompu, ou que le temps d'attente spécifié s'écoule.

4

public long awaitUninterruptibly()

Fait attendre le thread actuel jusqu'à ce qu'il soit signalé.

5

public long awaitUntil()

Fait attendre le thread actuel jusqu'à ce qu'il soit signalé ou interrompu, ou que le délai spécifié soit écoulé.

6

public void signal()

Réveille un fil d'attente.

sept

public void signalAll()

Réveille tous les threads en attente.

Exemple

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestThread {

   public static void main(String[] args) throws InterruptedException {
      ItemQueue itemQueue = new ItemQueue(10);

      //Create a producer and a consumer.
      Thread producer = new Producer(itemQueue);
      Thread consumer = new Consumer(itemQueue);

      //Start both threads.
      producer.start();
      consumer.start();

      //Wait for both threads to terminate.
      producer.join();
      consumer.join();
   }

   static class ItemQueue {
      private Object[] items = null;
      private int current = 0;
      private int placeIndex = 0;
      private int removeIndex = 0;

      private final Lock lock;
      private final Condition isEmpty;
      private final Condition isFull;

      public ItemQueue(int capacity) {
         this.items = new Object[capacity];
         lock = new ReentrantLock();
         isEmpty = lock.newCondition();
         isFull = lock.newCondition();
      }

      public void add(Object item) throws InterruptedException {
         lock.lock();

         while(current >= items.length)
            isFull.await();

         items[placeIndex] = item;
         placeIndex = (placeIndex + 1) % items.length;
         ++current;

         //Notify the consumer that there is data available.
         isEmpty.signal();
         lock.unlock();
      }

      public Object remove() throws InterruptedException {
         Object item = null;

         lock.lock();

         while(current <= 0) {
            isEmpty.await();
         }
         item = items[removeIndex];
         removeIndex = (removeIndex + 1) % items.length;
         --current;

         //Notify the producer that there is space available.
         isFull.signal();
         lock.unlock();

         return item;
      }

      public boolean isEmpty() {
         return (items.length == 0);
      }
   }

   static class Producer extends Thread {
      private final ItemQueue queue;
      
      public Producer(ItemQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         String[] numbers =
            {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"};

         try {
            
            for(String number: numbers) {
               System.out.println("[Producer]: " + number);
            }
            queue.add(null);
         } catch (InterruptedException ex) {
            ex.printStackTrace();
         } 
      }
   }

   static class Consumer extends Thread {
      private final ItemQueue queue;
      
      public Consumer(ItemQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         
         try {
            
            do {
               Object number = queue.remove();
               System.out.println("[Consumer]: " + number);

               if(number == null) {
                  return;
               }
            } while(!queue.isEmpty());
         } catch (InterruptedException ex) {
            ex.printStackTrace();
         }
      }
   }
}

Cela produira le résultat suivant.

Production

[Producer]: 1
[Producer]: 2
[Producer]: 3
[Producer]: 4
[Producer]: 5
[Producer]: 6
[Producer]: 7
[Producer]: 8
[Producer]: 9
[Producer]: 10
[Producer]: 11
[Producer]: 12
[Consumer]: null

Une classe java.util.concurrent.atomic.AtomicInteger fournit des opérations sur la valeur int sous-jacente qui peuvent être lues et écrites de manière atomique, et contient également des opérations atomiques avancées. AtomicInteger prend en charge les opérations atomiques sur la variable int sous-jacente. Il a des méthodes get et set qui fonctionnent comme des lectures et des écritures sur des variables volatiles. Autrement dit, un ensemble a une relation qui se produit avant avec tout get ultérieur sur la même variable. La méthode atomic compareAndSet possède également ces fonctionnalités de cohérence de mémoire.

Méthodes AtomicInteger

Voici la liste des méthodes importantes disponibles dans la classe AtomicInteger.

Sr.No. Méthode et description
1

public int addAndGet(int delta)

Ajoute atomiquement la valeur donnée à la valeur actuelle.

2

public boolean compareAndSet(int expect, int update)

Définit atomiquement la valeur sur la valeur mise à jour donnée si la valeur actuelle est la même que la valeur attendue.

3

public int decrementAndGet()

Décrémente atomiquement de un la valeur actuelle.

4

public double doubleValue()

Renvoie la valeur du nombre spécifié sous forme de double.

5

public float floatValue()

Renvoie la valeur du nombre spécifié sous forme de flottant.

6

public int get()

Obtient la valeur actuelle.

sept

public int getAndAdd(int delta)

Atomiclly ajoute la valeur donnée à la valeur actuelle.

8

public int getAndDecrement()

Décrémente atomiquement de un la valeur actuelle.

9

public int getAndIncrement()

Incrémente atomiquement de un la valeur actuelle.

dix

public int getAndSet(int newValue)

Définit atomiquement la valeur donnée et renvoie l'ancienne valeur.

11

public int incrementAndGet()

Incrémente atomiquement de un la valeur actuelle.

12

public int intValue()

Renvoie la valeur du nombre spécifié sous la forme d'un entier.

13

public void lazySet(int newValue)

Définit finalement la valeur donnée.

14

public long longValue()

Renvoie la valeur du nombre spécifié sous la forme d'un long.

15

public void set(int newValue)

Définit la valeur donnée.

16

public String toString()

Renvoie la représentation String de la valeur actuelle.

17

public boolean weakCompareAndSet(int expect, int update)

Définit atomiquement la valeur sur la valeur mise à jour donnée si la valeur actuelle est la même que la valeur attendue.

Exemple

Le programme TestThread suivant montre une implémentation non sécurisée du compteur dans un environnement basé sur les threads.

public class TestThread {

   static class Counter {
      private int c = 0;

      public void increment() {
         c++;
      }

      public int value() {
         return c;
      }
   }
   
   public static void main(final String[] arguments) throws InterruptedException {
      final Counter counter = new Counter();
      
      //1000 threads
      for(int i = 0; i < 1000 ; i++) {
         
         new Thread(new Runnable() {
            
            public void run() {
               counter.increment();
            }
         }).start(); 
      }  
      Thread.sleep(6000);
      System.out.println("Final number (should be 1000): " + counter.value());
   }  
}

Cela peut produire le résultat suivant en fonction de la vitesse de l'ordinateur et de l'entrelacement des threads.

Production

Final number (should be 1000): 1000

Exemple

import java.util.concurrent.atomic.AtomicInteger;

public class TestThread {

   static class Counter {
      private AtomicInteger c = new AtomicInteger(0);

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

      public int value() {
         return c.get();
      }
   }
   
   public static void main(final String[] arguments) throws InterruptedException {
      final Counter counter = new Counter();
      
      //1000 threads
      for(int i = 0; i < 1000 ; i++) {

         new Thread(new Runnable() {
            public void run() {
               counter.increment();
            }
         }).start(); 
      }  
      Thread.sleep(6000);
      System.out.println("Final number (should be 1000): " + counter.value());
   }
}

Cela produira le résultat suivant.

Production

Final number (should be 1000): 1000

Une classe java.util.concurrent.atomic.AtomicLong fournit des opérations sur la valeur longue sous-jacente qui peuvent être lues et écrites de manière atomique, et contient également des opérations atomiques avancées. AtomicLong prend en charge les opérations atomiques sur la variable longue sous-jacente. Il a des méthodes get et set qui fonctionnent comme des lectures et des écritures sur des variables volatiles. Autrement dit, un ensemble a une relation qui se produit avant avec tout get ultérieur sur la même variable. La méthode atomic compareAndSet possède également ces fonctionnalités de cohérence de mémoire.

Méthodes AtomicLong

Voici la liste des méthodes importantes disponibles dans la classe AtomicLong.

Sr.No. Méthode et description
1

public long addAndGet(long delta)

Ajoute atomiquement la valeur donnée à la valeur actuelle.

2

public boolean compareAndSet(long expect, long update)

Définit atomiquement la valeur sur la valeur mise à jour donnée si la valeur actuelle est la même que la valeur attendue.

3

public long decrementAndGet()

Décrémente atomiquement de un la valeur actuelle.

4

public double doubleValue()

Renvoie la valeur du nombre spécifié sous forme de double.

5

public float floatValue()

Renvoie la valeur du nombre spécifié sous forme de flottant.

6

public long get()

Obtient la valeur actuelle.

sept

public long getAndAdd(long delta)

Atomiclly ajoute la valeur donnée à la valeur actuelle.

8

public long getAndDecrement()

Décrémente atomiquement de un la valeur actuelle.

9

public long getAndIncrement()

Incrémente atomiquement de un la valeur actuelle.

dix

public long getAndSet(long newValue)

Définit atomiquement la valeur donnée et renvoie l'ancienne valeur.

11

public long incrementAndGet()

Incrémente atomiquement de un la valeur actuelle.

12

public int intValue()

Renvoie la valeur du nombre spécifié sous la forme d'un entier.

13

public void lazySet(long newValue)

Définit finalement la valeur donnée.

14

public long longValue()

Renvoie la valeur du nombre spécifié sous la forme d'un long.

15

public void set(long newValue)

Définit la valeur donnée.

16

public String toString()

Renvoie la représentation String de la valeur actuelle.

17

public boolean weakCompareAndSet(long expect, long update)

Définit atomiquement la valeur sur la valeur mise à jour donnée si la valeur actuelle est la même que la valeur attendue.

Exemple

Le programme TestThread suivant montre une implémentation sûre du compteur à l'aide d'AtomicLong dans un environnement basé sur les threads.

import java.util.concurrent.atomic.AtomicLong;

public class TestThread {

   static class Counter {
      private AtomicLong c = new AtomicLong(0);

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

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

   public static void main(final String[] arguments) throws InterruptedException {
      final Counter counter = new Counter();
      
      //1000 threads
      for(int i = 0; i < 1000 ; i++) {
         
         new Thread(new Runnable() {
            
            public void run() {
               counter.increment();
            }
         }).start();	
      }
      Thread.sleep(6000);			   		  
      System.out.println("Final number (should be 1000): " + counter.value());
   }
}

Cela produira le résultat suivant.

Production

Final number (should be 1000): 1000

Une classe java.util.concurrent.atomic.AtomicBoolean fournit des opérations sur la valeur booléenne sous-jacente qui peuvent être lues et écrites de manière atomique, et contient également des opérations atomiques avancées. AtomicBoolean prend en charge les opérations atomiques sur la variable booléenne sous-jacente. Il a des méthodes get et set qui fonctionnent comme des lectures et des écritures sur des variables volatiles. Autrement dit, un ensemble a une relation qui se produit avant avec tout get ultérieur sur la même variable. La méthode atomic compareAndSet possède également ces fonctionnalités de cohérence de mémoire.

Méthodes AtomicBoolean

Voici la liste des méthodes importantes disponibles dans la classe AtomicBoolean.

Sr.No. Méthode et description
1

public boolean compareAndSet(boolean expect, boolean update)

Définit atomiquement la valeur sur la valeur mise à jour donnée si la valeur actuelle == la valeur attendue.

2

public boolean get()

Renvoie la valeur actuelle.

3

public boolean getAndSet(boolean newValue)

Définit atomiquement la valeur donnée et renvoie la valeur précédente.

4

public void lazySet(boolean newValue)

Définit finalement la valeur donnée.

5

public void set(boolean newValue)

Définit inconditionnellement la valeur donnée.

6

public String toString()

Renvoie la représentation String de la valeur actuelle.

sept

public boolean weakCompareAndSet(boolean expect, boolean update)

Définit atomiquement la valeur sur la valeur mise à jour donnée si la valeur actuelle == la valeur attendue.

Exemple

Le programme TestThread suivant montre l'utilisation de la variable AtomicBoolean dans un environnement basé sur les threads.

import java.util.concurrent.atomic.AtomicBoolean;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final AtomicBoolean atomicBoolean = new AtomicBoolean(false);

      new Thread("Thread 1") {

         public void run() {

            while(true) {
               System.out.println(Thread.currentThread().getName() 
                  +" Waiting for Thread 2 to set Atomic variable to true. Current value is "
                  + atomicBoolean.get());

               if(atomicBoolean.compareAndSet(true, false)) {
                  System.out.println("Done!");
                  break;
               }
            }
         };
      }.start();

      new Thread("Thread 2") {

         public void run() {
            System.out.println(Thread.currentThread().getName() +
               ", Atomic Variable: " +atomicBoolean.get()); 
            System.out.println(Thread.currentThread().getName() +
               " is setting the variable to true ");
            atomicBoolean.set(true);
            System.out.println(Thread.currentThread().getName() +
               ", Atomic Variable: " +atomicBoolean.get()); 
         };
      }.start();
   }
}

Cela produira le résultat suivant.

Production

Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 2, Atomic Variable: false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 2 is setting the variable to true
Thread 2, Atomic Variable: true
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Done!

Une classe java.util.concurrent.atomic.AtomicReference fournit des opérations sur la référence d'objet sous-jacente qui peuvent être lues et écrites de manière atomique, et contient également des opérations atomiques avancées. AtomicReference prend en charge les opérations atomiques sur la variable de référence d'objet sous-jacente. Il a des méthodes get et set qui fonctionnent comme des lectures et des écritures sur des variables volatiles. Autrement dit, un ensemble a une relation qui se produit avant avec tout get ultérieur sur la même variable. La méthode atomic compareAndSet possède également ces fonctionnalités de cohérence de mémoire.

Méthodes AtomicReference

Voici la liste des méthodes importantes disponibles dans la classe AtomicReference.

Sr.No. Méthode et description
1

public boolean compareAndSet(V expect, V update)

Définit atomiquement la valeur sur la valeur mise à jour donnée si la valeur actuelle == la valeur attendue.

2

public boolean get()

Renvoie la valeur actuelle.

3

public boolean getAndSet(V newValue)

Définit atomiquement la valeur donnée et renvoie la valeur précédente.

4

public void lazySet(V newValue)

Définit finalement la valeur donnée.

5

public void set(V newValue)

Définit inconditionnellement la valeur donnée.

6

public String toString()

Renvoie la représentation String de la valeur actuelle.

sept

public boolean weakCompareAndSet(V expect, V update)

Définit atomiquement la valeur sur la valeur mise à jour donnée si la valeur actuelle == la valeur attendue.

Exemple

Le programme TestThread suivant montre l'utilisation de la variable AtomicReference dans un environnement basé sur les threads.

import java.util.concurrent.atomic.AtomicReference;

public class TestThread {
   private static String message = "hello";
   private static AtomicReference<String> atomicReference;

   public static void main(final String[] arguments) throws InterruptedException {
      atomicReference = new AtomicReference<String>(message);
      
      new Thread("Thread 1") {
         
         public void run() {
            atomicReference.compareAndSet(message, "Thread 1");
            message = message.concat("-Thread 1!");
         };
      }.start();

      System.out.println("Message is: " + message);
      System.out.println("Atomic Reference of Message is: " + atomicReference.get());
   }
}

Cela produira le résultat suivant.

Production

Message is: hello
Atomic Reference of Message is: Thread 1

Une classe java.util.concurrent.atomic.AtomicIntegerArray fournit des opérations sur le tableau int sous-jacent qui peuvent être lues et écrites de manière atomique, et contient également des opérations atomiques avancées. AtomicIntegerArray prend en charge les opérations atomiques sur la variable de tableau int sous-jacente. Il a des méthodes get et set qui fonctionnent comme des lectures et des écritures sur des variables volatiles. Autrement dit, un ensemble a une relation qui se produit avant avec tout get ultérieur sur la même variable. La méthode atomic compareAndSet possède également ces fonctionnalités de cohérence de mémoire.

Méthodes AtomicIntegerArray

Voici la liste des méthodes importantes disponibles dans la classe AtomicIntegerArray.

Sr.No. Méthode et description
1

public int addAndGet(int i, int delta)

Ajoute atomiquement la valeur donnée à l'élément à l'index i.

2

public boolean compareAndSet(int i, int expect, int update)

Définit atomiquement l'élément à la position i à la valeur mise à jour donnée si la valeur actuelle == la valeur attendue.

3

public int decrementAndGet(int i)

Décrémente atomiquement de un l'élément à l'index i.

4

public int get(int i)

Obtient la valeur actuelle à la position i.

5

public int getAndAdd(int i, int delta)

Ajoute atomiquement la valeur donnée à l'élément à l'index i.

6

public int getAndDecrement(int i)

Décrémente atomiquement de un l'élément à l'index i.

sept

public int getAndIncrement(int i)

Incrémente atomiquement de un l'élément à l'index i.

8

public int getAndSet(int i, int newValue)

Définit atomiquement l'élément à la position i à la valeur donnée et renvoie l'ancienne valeur.

9

public int incrementAndGet(int i)

Incrémente atomiquement de un l'élément à l'index i.

dix

public void lazySet(int i, int newValue)

Définit finalement l'élément à la position i à la valeur donnée.

11

public int length()

Renvoie la longueur du tableau.

12

public void set(int i, int newValue)

Définit l'élément à la position i à la valeur donnée.

13

public String toString()

Renvoie la représentation String des valeurs actuelles du tableau.

14

public boolean weakCompareAndSet(int i, int expect, int update)

Définit atomiquement l'élément à la position i à la valeur mise à jour donnée si la valeur actuelle == la valeur attendue.

Exemple

Le programme TestThread suivant montre l'utilisation de la variable AtomicIntegerArray dans un environnement basé sur les threads.

import java.util.concurrent.atomic.AtomicIntegerArray;

public class TestThread {
   private static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);

   public static void main(final String[] arguments) throws InterruptedException {
      
      for (int i = 0; i<atomicIntegerArray.length(); i++) {
         atomicIntegerArray.set(i, 1);
      }

      Thread t1 = new Thread(new Increment());
      Thread t2 = new Thread(new Compare());
      t1.start();
      t2.start();

      t1.join();
      t2.join();

      System.out.println("Values: ");

      for (int i = 0; i<atomicIntegerArray.length(); i++) {
         System.out.print(atomicIntegerArray.get(i) + " ");
      }
   }

   static class Increment implements Runnable {

      public void run() {

         for(int i = 0; i<atomicIntegerArray.length(); i++) {
            int add = atomicIntegerArray.incrementAndGet(i);
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ add);
         }
      }
   }

   static class Compare implements Runnable {

      public void run() {

         for(int i = 0; i<atomicIntegerArray.length(); i++) {
            boolean swapped = atomicIntegerArray.compareAndSet(i, 2, 3);
            
            if(swapped) {
               System.out.println("Thread " + Thread.currentThread().getId()
                  + ", index " +i + ", value: 3");
            }
         }
      }
   }
}

Cela produira le résultat suivant.

Production

Thread 10, index 0, value: 2
Thread 10, index 1, value: 2
Thread 10, index 2, value: 2
Thread 11, index 0, value: 3
Thread 10, index 3, value: 2
Thread 11, index 1, value: 3
Thread 11, index 2, value: 3
Thread 10, index 4, value: 2
Thread 11, index 3, value: 3
Thread 10, index 5, value: 2
Thread 10, index 6, value: 2
Thread 11, index 4, value: 3
Thread 10, index 7, value: 2
Thread 11, index 5, value: 3
Thread 10, index 8, value: 2
Thread 11, index 6, value: 3
Thread 10, index 9, value: 2
Thread 11, index 7, value: 3
Thread 11, index 8, value: 3
Thread 11, index 9, value: 3
Values:
3 3 3 3 3 3 3 3 3 3

Une classe java.util.concurrent.atomic.AtomicLongArray fournit des opérations sur le long tableau sous-jacent qui peuvent être lues et écrites de manière atomique, et contient également des opérations atomiques avancées. AtomicLongArray prend en charge les opérations atomiques sur la variable de tableau long sous-jacente. Il a des méthodes get et set qui fonctionnent comme des lectures et des écritures sur des variables volatiles. Autrement dit, un ensemble a une relation qui se produit avant avec tout get ultérieur sur la même variable. La méthode atomic compareAndSet possède également ces fonctionnalités de cohérence de mémoire.

Méthodes AtomicLongArray

Voici la liste des méthodes importantes disponibles dans la classe AtomicLongArray.

Sr.No. Méthode et description
1

public long addAndGet(int i, long delta)

Ajoute atomiquement la valeur donnée à l'élément à l'index i.

2

public boolean compareAndSet(int i, long expect, long update)

Définit atomiquement l'élément à la position i à la valeur mise à jour donnée si la valeur actuelle == la valeur attendue.

3

public long decrementAndGet(int i)

Décrémente atomiquement de un l'élément à l'index i.

4

public long get(int i)

Obtient la valeur actuelle à la position i.

5

public long getAndAdd(int i, long delta)

Ajoute atomiquement la valeur donnée à l'élément à l'index i.

6

public long getAndDecrement(int i)

Décrémente atomiquement de un l'élément à l'index i.

sept

public long getAndIncrement(int i)

Incrémente atomiquement de un l'élément à l'index i.

8

public long getAndSet(int i, long newValue)

Définit atomiquement l'élément à la position i à la valeur donnée et renvoie l'ancienne valeur.

9

public long incrementAndGet(int i)

Incrémente atomiquement de un l'élément à l'index i.

dix

public void lazySet(int i, long newValue)

Définit finalement l'élément à la position i à la valeur donnée.

11

public int length()

Renvoie la longueur du tableau.

12

public void set(int i, long newValue)

Définit l'élément à la position i à la valeur donnée.

13

public String toString()

Renvoie la représentation String des valeurs actuelles du tableau.

14

public boolean weakCompareAndSet(int i, long expect, long update)

Définit atomiquement l'élément à la position i à la valeur mise à jour donnée si la valeur actuelle == la valeur attendue.

Exemple

Le programme TestThread suivant montre l'utilisation de la variable AtomicIntegerArray dans un environnement basé sur les threads.

import java.util.concurrent.atomic.AtomicLongArray;

public class TestThread {
   private static AtomicLongArray atomicLongArray = new AtomicLongArray(10);

   public static void main(final String[] arguments) throws InterruptedException {

      for (int i = 0; i<atomicLongArray.length(); i++) {
         atomicLongArray.set(i, 1);
      }

      Thread t1 = new Thread(new Increment());
      Thread t2 = new Thread(new Compare());
      t1.start();
      t2.start();

      t1.join();
      t2.join();

      System.out.println("Values: ");
      
      for (int i = 0; i<atomicLongArray.length(); i++) {
         System.out.print(atomicLongArray.get(i) + " ");
      }
   }  

   static class Increment implements Runnable {

      public void run() {

         for(int i = 0; i<atomicLongArray.length(); i++) {
            long add = atomicLongArray.incrementAndGet(i);
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ add);
         }
      }
   }

   static class Compare implements Runnable {

      public void run() {

         for(int i = 0; i<atomicLongArray.length(); i++) {
            boolean swapped = atomicLongArray.compareAndSet(i, 2, 3);
            
            if(swapped) {
               System.out.println("Thread " + Thread.currentThread().getId()
                  + ", index " +i + ", value: 3");
            }
         }
      }
   }
}

Cela produira le résultat suivant.

Production

Thread 9, index 0, value: 2
Thread 10, index 0, value: 3
Thread 9, index 1, value: 2
Thread 9, index 2, value: 2
Thread 9, index 3, value: 2
Thread 9, index 4, value: 2
Thread 10, index 1, value: 3
Thread 9, index 5, value: 2
Thread 10, index 2, value: 3
Thread 9, index 6, value: 2
Thread 10, index 3, value: 3
Thread 9, index 7, value: 2
Thread 10, index 4, value: 3
Thread 9, index 8, value: 2
Thread 9, index 9, value: 2
Thread 10, index 5, value: 3
Thread 10, index 6, value: 3
Thread 10, index 7, value: 3
Thread 10, index 8, value: 3
Thread 10, index 9, value: 3
Values: 
3 3 3 3 3 3 3 3 3 3

Une classe java.util.concurrent.atomic.AtomicReferenceArray fournit des opérations sur le tableau de référence sous-jacent qui peuvent être lues et écrites de manière atomique, et contient également des opérations atomiques avancées. AtomicReferenceArray prend en charge les opérations atomiques sur la variable de tableau de référence sous-jacente. Il a des méthodes get et set qui fonctionnent comme des lectures et des écritures sur des variables volatiles. Autrement dit, un ensemble a une relation qui se produit avant avec tout get ultérieur sur la même variable. La méthode atomic compareAndSet possède également ces fonctionnalités de cohérence de mémoire.

Méthodes AtomicReferenceArray

Voici la liste des méthodes importantes disponibles dans la classe AtomicReferenceArray.

Sr.No. Méthode et description
1

public boolean compareAndSet(int i, E expect, E update)

Définit atomiquement l'élément à la position i à la valeur mise à jour donnée si la valeur actuelle == la valeur attendue.

2

public E get(int i)

Obtient la valeur actuelle à la position i.

3

public E getAndSet(int i, E newValue)

Définit atomiquement l'élément à la position i à la valeur donnée et renvoie l'ancienne valeur.

4

public void lazySet(int i, E newValue)

Définit finalement l'élément à la position i à la valeur donnée.

5

public int length()

Renvoie la longueur du tableau.

6

public void set(int i, E newValue)

Définit l'élément à la position i à la valeur donnée.

sept

public String toString()

Renvoie la représentation String des valeurs actuelles du tableau.

8

public boolean weakCompareAndSet(int i, E expect, E update)

Définit atomiquement l'élément à la position i à la valeur mise à jour donnée si la valeur actuelle == la valeur attendue.

Exemple

Le programme TestThread suivant montre l'utilisation de la variable AtomicReferenceArray dans un environnement basé sur les threads.

import java.util.concurrent.atomic.AtomicReferenceArray;

public class TestThread {
   private static String[] source = new String[10];
   private static AtomicReferenceArray<String> atomicReferenceArray 
      = new AtomicReferenceArray<String>(source);

   public static void main(final String[] arguments) throws InterruptedException {

      for (int i = 0; i<atomicReferenceArray.length(); i++) {
         atomicReferenceArray.set(i, "item-2");
      }

      Thread t1 = new Thread(new Increment());
      Thread t2 = new Thread(new Compare());
      t1.start();
      t2.start();

      t1.join();
      t2.join();		
   }  

   static class Increment implements Runnable {
      
      public void run() {
         
         for(int i = 0; i<atomicReferenceArray.length(); i++) {
            String add = atomicReferenceArray.getAndSet(i,"item-"+ (i+1));
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ add);
         }
      }
   }

   static class Compare implements Runnable {
      
      public void run() {
         
         for(int i = 0; i<atomicReferenceArray.length(); i++) {
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ atomicReferenceArray.get(i));
            boolean swapped = atomicReferenceArray.compareAndSet(i, "item-2", "updated-item-2");
            System.out.println("Item swapped: " + swapped);
            
            if(swapped) {
               System.out.println("Thread " + Thread.currentThread().getId() 
                  + ", index " +i + ", updated-item-2");
            }
         }
      }
   }
}

Cela produira le résultat suivant.

Production

Thread 9, index 0, value: item-2
Thread 10, index 0, value: item-1
Item swapped: false
Thread 10, index 1, value: item-2
Item swapped: true
Thread 9, index 1, value: updated-item-2
Thread 10, index 1, updated-item-2
Thread 10, index 2, value: item-3
Item swapped: false
Thread 10, index 3, value: item-2
Item swapped: true
Thread 10, index 3, updated-item-2
Thread 10, index 4, value: item-2
Item swapped: true
Thread 10, index 4, updated-item-2
Thread 10, index 5, value: item-2
Item swapped: true
Thread 10, index 5, updated-item-2
Thread 10, index 6, value: item-2
Thread 9, index 2, value: item-2
Item swapped: true
Thread 9, index 3, value: updated-item-2
Thread 10, index 6, updated-item-2
Thread 10, index 7, value: item-2
Thread 9, index 4, value: updated-item-2
Item swapped: true
Thread 9, index 5, value: updated-item-2
Thread 10, index 7, updated-item-2
Thread 9, index 6, value: updated-item-2
Thread 10, index 8, value: item-2
Thread 9, index 7, value: updated-item-2
Item swapped: true
Thread 9, index 8, value: updated-item-2
Thread 10, index 8, updated-item-2
Thread 9, index 9, value: item-2
Thread 10, index 9, value: item-10
Item swapped: false

Une interface java.util.concurrent.Executor est une interface simple pour prendre en charge le lancement de nouvelles tâches.

Méthodes ExecutorService

Sr.No. Méthode et description
1

void execute(Runnable command)

Exécute la commande donnée à un moment donné dans le futur.

Exemple

Le programme TestThread suivant montre l'utilisation de l'interface Executor dans un environnement basé sur les threads.

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      Executor executor = Executors.newCachedThreadPool();
      executor.execute(new Task());
      ThreadPoolExecutor pool = (ThreadPoolExecutor)executor;
      pool.shutdown();
   }  

   static class Task implements Runnable {
      
      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task!");
            TimeUnit.SECONDS.sleep(duration);
            System.out.println("Task Completed");
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Cela produira le résultat suivant.

Production

Running Task!
Task Completed

Une interface java.util.concurrent.ExecutorService est une sous-interface de l'interface Executor et ajoute des fonctionnalités pour gérer le cycle de vie, à la fois des tâches individuelles et de l'exécuteur lui-même.

Méthodes ExecutorService

Sr.No. Méthode et description
1

boolean awaitTermination(long timeout, TimeUnit unit)

Bloque jusqu'à ce que toutes les tâches soient exécutées après une demande d'arrêt, ou que le délai d'expiration se produise ou que le thread en cours soit interrompu, selon la première éventualité.

2

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)

Exécute les tâches données, renvoyant une liste de Futures conservant leur statut et leurs résultats lorsque tout est terminé.

3

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

Exécute les tâches données, renvoyant une liste de Futures conservant leur statut et leurs résultats lorsque tout est terminé ou que le délai expire, selon la première éventualité.

4

<T> T invokeAny(Collection<? extends Callable<T>> tasks)

Exécute les tâches données, en retournant le résultat d'une tâche qui s'est terminée avec succès (c'est-à-dire sans lever d'exception), le cas échéant.

5

<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

Exécute les tâches données, en retournant le résultat d'une tâche qui s'est terminée avec succès (c'est-à-dire sans lever d'exception), le cas échéant avant l'expiration du délai imparti.
6

boolean isShutdown()

Renvoie true si cet exécuteur a été arrêté.

sept

boolean isTerminated()

Renvoie true si toutes les tâches sont terminées après l'arrêt.

8

void shutdown()

Lance un arrêt ordonné dans lequel les tâches précédemment soumises sont exécutées, mais aucune nouvelle tâche ne sera acceptée.

9

List<Runnable> shutdownNow()

Tente d'arrêter toutes les tâches en cours d'exécution, interrompt le traitement des tâches en attente et renvoie une liste des tâches en attente d'exécution.

dix

<T> Future<T> submit(Callable<T> task)

Soumet une tâche de retour de valeur pour exécution et renvoie un Future représentant les résultats en attente de la tâche.

11

Future<?> submit(Runnable task)

Soumet une tâche exécutable pour exécution et renvoie un Future représentant cette tâche.

12

<T> Future<T> submit(Runnable task, T result)

Soumet une tâche exécutable pour exécution et renvoie un Future représentant cette tâche.

Exemple

Le programme TestThread suivant montre l'utilisation de l'interface ExecutorService dans un environnement basé sur les threads.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newSingleThreadExecutor();

      try {
         executor.submit(new Task());
         System.out.println("Shutdown executor");
         executor.shutdown();
         executor.awaitTermination(5, TimeUnit.SECONDS);
      } catch (InterruptedException e) {
         System.err.println("tasks interrupted");
      } finally {

         if (!executor.isTerminated()) {
            System.err.println("cancel non-finished tasks");
         }
         executor.shutdownNow();
         System.out.println("shutdown finished");
      }
   }

   static class Task implements Runnable {
      
      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 20);
            System.out.println("Running Task!");
            TimeUnit.SECONDS.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }	   
}

Cela produira le résultat suivant.

Production

Shutdown executor
Running Task!
shutdown finished
cancel non-finished tasks
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:302)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:328)
	at TestThread$Task.run(TestThread.java:39)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
	at java.lang.Thread.run(Thread.java:662)

Une interface java.util.concurrent.ScheduledExecutorService est une sous-interface de l'interface ExecutorService et prend en charge l'exécution future et / ou périodique des tâches.

Méthodes ScheduledExecutorService

Sr.No. Méthode et description
1

<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)

Crée et exécute un ScheduledFuture qui devient activé après le délai donné.

2

ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)

Crée et exécute une action ponctuelle qui devient activée après le délai donné.

3

ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

Crée et exécute une action périodique qui devient activée d'abord après le délai initial donné, puis avec la période donnée; c'est-à-dire que les exécutions commenceront après initialDelay puis initialDelay + period, puis initialDelay + 2 * period, et ainsi de suite.

4

ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

Crée et exécute une action périodique qui devient activée d'abord après le délai initial donné, puis avec le délai donné entre la fin d'une exécution et le début de la suivante.

Exemple

Le programme TestThread suivant montre l'utilisation de l'interface ScheduledExecutorService dans un environnement basé sur les threads.

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

      final ScheduledFuture<?> beepHandler = 
         scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);

      scheduler.schedule(new Runnable() {

         @Override
         public void run() {
            beepHandler.cancel(true);
            scheduler.shutdown();			
         }
      }, 10, TimeUnit.SECONDS);
   }

   static class BeepTask implements Runnable {
      
      public void run() {
         System.out.println("beep");      
      }
   }
}

Cela produira le résultat suivant.

Production

beep
beep
beep
beep

Un pool de threads fixe peut être obtenu en appelant la méthode statique newFixedThreadPool () de la classe Executors.

Syntaxe

ExecutorService fixedPool = Executors.newFixedThreadPool(2);

  • Un maximum de 2 threads sera actif pour traiter les tâches.

  • Si plus de 2 threads sont soumis, ils sont placés dans une file d'attente jusqu'à ce que les threads deviennent disponibles.

  • Un nouveau thread est créé pour prendre sa place si un thread se termine en raison d'un échec lors de l'exécution, l'arrêt sur l'exécuteur n'est pas encore appelé.

  • Tout thread existe jusqu'à ce que le pool soit arrêté.

Exemple

Le programme TestThread suivant montre l'utilisation de la méthode newFixedThreadPool dans un environnement basé sur les threads.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {
	
   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newFixedThreadPool(2);

      // Cast the object to its class type
      ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;

      //Stats before tasks execution
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.submit(new Task());
      executor.submit(new Task());

      //Stats after tasks execution
      System.out.println("Core threads: " + pool.getCorePoolSize());
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.shutdown();
   }  

   static class Task implements Runnable {

      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task! Thread Name: " +
               Thread.currentThread().getName());
               TimeUnit.SECONDS.sleep(duration);
            
            System.out.println("Task Completed! Thread Name: " +
               Thread.currentThread().getName());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Cela produira le résultat suivant.

Production

Largest executions: 0
Maximum allowed threads: 2
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 2
Largest executions: 2
Maximum allowed threads: 2
Current threads in pool: 2
Currently executing threads: 1
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-1
Running Task! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-1

Un pool de threads mis en cache peut être obtenu en appelant la méthode statique newCachedThreadPool () de la classe Executors.

Syntaxe

ExecutorService executor = Executors.newCachedThreadPool();

  • La méthode newCachedThreadPool crée un exécuteur ayant un pool de threads extensible.

  • Un tel exécuteur convient aux applications qui lancent de nombreuses tâches de courte durée.

Exemple

Le programme TestThread suivant montre l'utilisation de la méthode newCachedThreadPool dans un environnement basé sur les threads.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {
	
   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newCachedThreadPool();

      // Cast the object to its class type
      ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;

      //Stats before tasks execution
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.submit(new Task());
      executor.submit(new Task());

      //Stats after tasks execution
      System.out.println("Core threads: " + pool.getCorePoolSize());
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.shutdown();
   }  

   static class Task implements Runnable {

      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task! Thread Name: " +
               Thread.currentThread().getName());
               TimeUnit.SECONDS.sleep(duration);
            System.out.println("Task Completed! Thread Name: " +
               Thread.currentThread().getName());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Cela produira le résultat suivant.

Production

Largest executions: 0
Maximum allowed threads: 2147483647
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 0
Largest executions: 2
Maximum allowed threads: 2147483647
Current threads in pool: 2
Currently executing threads: 2
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-1
Running Task! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-1

Un pool de threads planifié peut être obtenu en appelant la méthode statique newScheduledThreadPool () de la classe Executors.

Syntaxe

ExecutorService executor = Executors.newScheduledThreadPool(1);

Exemple

Le programme TestThread suivant montre l'utilisation de la méthode newScheduledThreadPool dans un environnement basé sur les threads.

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

      final ScheduledFuture<?> beepHandler = 
         scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);

      scheduler.schedule(new Runnable() {

         @Override
         public void run() {
            beepHandler.cancel(true);
            scheduler.shutdown();			
         }
      }, 10, TimeUnit.SECONDS);
   }  

   static class BeepTask implements Runnable {

      public void run() {
         System.out.println("beep");      
      }
   }
}

Cela produira le résultat suivant.

Production

beep
beep
beep
beep

Un pool de threads unique peut être obtenu en appelant la méthode statique newSingleThreadExecutor () de la classe Executors.

Syntaxe

ExecutorService executor = Executors.newSingleThreadExecutor();

Où la méthode newSingleThreadExecutor crée un exécuteur qui exécute une seule tâche à la fois.

Exemple

Le programme TestThread suivant montre l'utilisation de la méthode newSingleThreadExecutor dans un environnement basé sur les threads.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newSingleThreadExecutor();

      try {
         executor.submit(new Task());
         System.out.println("Shutdown executor");
         executor.shutdown();
         executor.awaitTermination(5, TimeUnit.SECONDS);
      } catch (InterruptedException e) {
         System.err.println("tasks interrupted");
      } finally {

         if (!executor.isTerminated()) {
            System.err.println("cancel non-finished tasks");
         }
         executor.shutdownNow();
         System.out.println("shutdown finished");
      }
   }

   static class Task implements Runnable {
      
      public void run() {

         try {
            Long duration = (long) (Math.random() * 20);
            System.out.println("Running Task!");
            TimeUnit.SECONDS.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Cela produira le résultat suivant.

Production

Shutdown executor
Running Task!
shutdown finished
cancel non-finished tasks
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:302)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:328)
	at TestThread$Task.run(TestThread.java:39) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
	at java.lang.Thread.run(Thread.java:662)

java.util.concurrent.ThreadPoolExecutor est un ExecutorService pour exécuter chaque tâche soumise en utilisant l'un des éventuellement plusieurs threads regroupés, normalement configurés à l'aide des méthodes d'usine Executors. Il fournit également diverses méthodes utilitaires pour vérifier les statistiques des threads actuels et les contrôler.

Méthodes ThreadPoolExecutor

Sr.No. Méthode et description
1

protected void afterExecute(Runnable r, Throwable t)

Méthode appelée à la fin de l'exécution du Runnable donné.

2

void allowCoreThreadTimeOut(boolean value)

Définit la stratégie régissant si les threads principaux peuvent expirer et se terminer si aucune tâche n'arrive dans le délai de conservation, remplacés si nécessaire lorsque de nouvelles tâches arrivent.

3

boolean allowsCoreThreadTimeOut()

Renvoie true si ce pool permet aux threads principaux d'expirer et de se terminer si aucune tâche n'arrive dans le délai de keepAlive, remplacé si nécessaire lorsque de nouvelles tâches arrivent.

4

boolean awaitTermination(long timeout, TimeUnit unit)

Bloque jusqu'à ce que toutes les tâches soient exécutées après une demande d'arrêt, ou que le délai d'expiration se produise ou que le thread en cours soit interrompu, selon la première éventualité.

5

protected void beforeExecute(Thread t, Runnable r)

Méthode appelée avant d'exécuter le Runnable donné dans le thread donné.

6

void execute(Runnable command)

Exécute la tâche donnée ultérieurement.

sept

protected void finalize()

Appelle l'arrêt lorsque cet exécuteur n'est plus référencé et qu'il n'a pas de threads.

8

int getActiveCount()

Renvoie le nombre approximatif de threads qui exécutent activement des tâches.

9

long getCompletedTaskCount()

Renvoie le nombre total approximatif de tâches dont l'exécution est terminée.

dix

int getCorePoolSize()

Renvoie le nombre principal de threads.

11

long getKeepAliveTime(TimeUnit unit)

Renvoie la durée de conservation des threads, qui correspond à la durée pendant laquelle les threads dépassant la taille du pool de cœurs peuvent rester inactifs avant d'être arrêtés.

12

int getLargestPoolSize()

Renvoie le plus grand nombre de threads qui ont jamais été simultanément dans le pool.

13

int getMaximumPoolSize()

Renvoie le nombre maximal de threads autorisé.

14

int getPoolSize()

Renvoie le nombre actuel de threads dans le pool.

15

BlockingQueue getQueue()

Renvoie la file d'attente des tâches utilisée par cet exécuteur.

15

RejectedExecutionHandler getRejectedExecutionHandler()

Renvoie le gestionnaire actuel pour les tâches non exécutables.

16

long getTaskCount()

Renvoie le nombre total approximatif de tâches dont l'exécution a déjà été planifiée.

17

ThreadFactory getThreadFactory()

Renvoie la fabrique de threads utilisée pour créer de nouveaux threads.

18

boolean isShutdown()

Renvoie true si cet exécuteur a été arrêté.

19

boolean isTerminated()

Renvoie true si toutes les tâches sont terminées après l'arrêt.

20

boolean isTerminating()

Renvoie true si cet exécuteur est en train de se terminer après shutdown () ou shutdownNow () mais ne s'est pas complètement arrêté.

21

int prestartAllCoreThreads()

Démarre tous les threads principaux, les obligeant à attendre inutilement le travail.

22

boolean prestartCoreThread()

Démarre un thread principal, le faisant attendre inutilement le travail.

23

void purge()

Tente de supprimer de la file d'attente de travail toutes les tâches futures qui ont été annulées.

24

boolean remove(Runnable task)

Supprime cette tâche de la file d'attente interne de l'exécuteur si elle est présente, empêchant ainsi son exécution si elle n'a pas déjà démarré.

25

void setCorePoolSize(int corePoolSize)

Définit le nombre principal de threads.

26

void setKeepAliveTime(long time, TimeUnit unit)

Définit le délai pendant lequel les threads peuvent rester inactifs avant d'être arrêtés.

27

void setMaximumPoolSize(int maximumPoolSize)

Définit le nombre maximum autorisé de threads.

28

void setRejectedExecutionHandler(RejectedExecutionHandler handler)

Définit un nouveau gestionnaire pour les tâches non exécutables.

29

void setThreadFactory(ThreadFactory threadFactory)

Définit la fabrique de threads utilisée pour créer de nouveaux threads.

30

void shutdown()

Lance un arrêt ordonné dans lequel les tâches précédemment soumises sont exécutées, mais aucune nouvelle tâche ne sera acceptée.

31

List<Runnable> shutdownNow()

Tente d'arrêter toutes les tâches en cours d'exécution, interrompt le traitement des tâches en attente et renvoie une liste des tâches en attente d'exécution.

32

protected void terminated()

Méthode appelée lorsque l'exécuteur s'est arrêté.

33

String toString()

Renvoie une chaîne identifiant ce pool, ainsi que son état, y compris des indications sur l'état d'exécution et le nombre estimé de travailleurs et de tâches.

Exemple

Le programme TestThread suivant montre l'utilisation de l'interface ThreadPoolExecutor dans un environnement basé sur les threads.

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {
	
   public static void main(final String[] arguments) throws InterruptedException {
      ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newCachedThreadPool();

      //Stats before tasks execution
      System.out.println("Largest executions: "
         + executor.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + executor.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + executor.getPoolSize());
      System.out.println("Currently executing threads: "
         + executor.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + executor.getTaskCount());

      executor.submit(new Task());
      executor.submit(new Task());

      //Stats after tasks execution
      System.out.println("Core threads: " + executor.getCorePoolSize());
      System.out.println("Largest executions: "
         + executor.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + executor.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + executor.getPoolSize());
      System.out.println("Currently executing threads: "
         + executor.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + executor.getTaskCount());

      executor.shutdown();
   }  

   static class Task implements Runnable {

      public void run() {

         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task! Thread Name: " +
               Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(duration);
            System.out.println("Task Completed! Thread Name: " +
               Thread.currentThread().getName());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Cela produira le résultat suivant.

Production

Largest executions: 0
Maximum allowed threads: 2147483647
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 0
Largest executions: 2
Maximum allowed threads: 2147483647
Current threads in pool: 2
Currently executing threads: 2
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-2
Running Task! Thread Name: pool-1-thread-1
Task Completed! Thread Name: pool-1-thread-1
Task Completed! Thread Name: pool-1-thread-2

java.util.concurrent.ScheduledThreadPoolExecutor est une sous-classe de ThreadPoolExecutor et peut en outre planifier des commandes à exécuter après un délai donné ou à s'exécuter périodiquement.

Méthodes ScheduledThreadPoolExecutor

Sr.No. Méthode et description
1

protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task)

Modifie ou remplace la tâche utilisée pour exécuter un appelable.

2

protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task)

Modifie ou remplace la tâche utilisée pour exécuter un exécutable.

3

void execute(Runnable command)

Exécute la commande avec zéro délai requis.

4

boolean getContinueExistingPeriodicTasksAfterShutdownPolicy()

Obtient la stratégie indiquant s'il faut continuer à exécuter les tâches périodiques existantes même lorsque cet exécuteur a été arrêté.

5

boolean getExecuteExistingDelayedTasksAfterShutdownPolicy()

Obtient la stratégie d'exécution des tâches différées existantes même lorsque cet exécuteur a été arrêté.

6

BlockingQueue<Runnable> getQueue()

Renvoie la file d'attente des tâches utilisée par cet exécuteur.

sept

boolean getRemoveOnCancelPolicy()

Obtient la stratégie indiquant si les tâches annulées doivent être immédiatement supprimées de la file d'attente de travail au moment de l'annulation.

8

<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)

Crée et exécute un ScheduledFuture qui devient activé après le délai donné.

9

ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)

Crée et exécute une action ponctuelle qui devient activée après le délai donné.

dix

ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

Crée et exécute une action périodique qui devient activée d'abord après le délai initial donné, puis avec la période donnée; c'est-à-dire que les exécutions commenceront après initialDelay puis initialDelay + period, puis initialDelay + 2 * period, et ainsi de suite.

11

ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

Crée et exécute une action périodique qui devient activée d'abord après le délai initial donné, puis avec le délai donné entre la fin d'une exécution et le début de la suivante.

12

void setContinueExistingPeriodicTasksAfterShutdownPolicy (boolean value)

Définit la stratégie sur la poursuite de l'exécution des tâches périodiques existantes même lorsque cet exécuteur a été arrêté.

13

void setExecuteExistingDelayedTasksAfterShutdownPolicy (boolean value)

Définit la stratégie d'exécution des tâches différées existantes même lorsque cet exécuteur a été arrêté.

14

void setRemoveOnCancelPolicy(boolean value)

Définit la stratégie selon laquelle les tâches annulées doivent être immédiatement supprimées de la file d'attente de travail au moment de l'annulation.

15

void shutdown()

Lance un arrêt ordonné dans lequel les tâches précédemment soumises sont exécutées, mais aucune nouvelle tâche ne sera acceptée.

16

List<Runnable> shutdownNow()

Tente d'arrêter toutes les tâches en cours d'exécution, interrompt le traitement des tâches en attente et renvoie une liste des tâches en attente d'exécution.

17

<T> Future<T> submit(Callable<T> task)

Soumet une tâche de retour de valeur pour exécution et renvoie un Future représentant les résultats en attente de la tâche.

18

Future<?> submit(Runnable task)

Soumet une tâche exécutable pour exécution et renvoie un Future représentant cette tâche.

19

<T> Future<T> submit(Runnable task, T result)

Soumet une tâche exécutable pour exécution et renvoie un Future représentant cette tâche.

Exemple

Le programme TestThread suivant montre l'utilisation de l'interface ScheduledThreadPoolExecutor dans un environnement basé sur les threads.

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final ScheduledThreadPoolExecutor scheduler = 
         (ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(1);

      final ScheduledFuture<?> beepHandler = 
         scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);

      scheduler.schedule(new Runnable() {

         @Override
         public void run() {
            beepHandler.cancel(true);
            scheduler.shutdown();			
         }
      }, 10, TimeUnit.SECONDS);
   }  

   static class BeepTask implements Runnable {
      
      public void run() {
         System.out.println("beep");      
      }
   }
}

Cela produira le résultat suivant.

Production

beep
beep
beep
beep

L'objet java.util.concurrent.Callable peut renvoyer le résultat calculé effectué par un thread contrairement à l'interface exécutable qui ne peut exécuter que le thread. L'objet Callable renvoie l'objet Future qui fournit des méthodes pour surveiller la progression d'une tâche exécutée par un thread. L'objet futur peut être utilisé pour vérifier l'état d'un appelable, puis récupérer le résultat de l'appelable une fois le thread terminé. Il fournit également une fonctionnalité de temporisation.

Syntaxe

//submit the callable using ThreadExecutor
//and get the result as a Future object
Future<Long> result10 = executor.submit(new FactorialService(10));
 
//get the result using get method of the Future object
//get method waits till the thread execution and then return the result of the execution.
Long factorial10 = result10.get();

Exemple

Le programme TestThread suivant montre l'utilisation de Futures et Callables dans un environnement basé sur les threads.

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException,
      ExecutionException {

      ExecutorService executor = Executors.newSingleThreadExecutor();

      System.out.println("Factorial Service called for 10!");
      Future<Long> result10 = executor.submit(new FactorialService(10));

      System.out.println("Factorial Service called for 20!");
      Future<Long> result20 = executor.submit(new FactorialService(20));

      Long factorial10 = result10.get();
      System.out.println("10! = " + factorial10);

      Long factorial20 = result20.get();
      System.out.println("20! = " + factorial20);

      executor.shutdown();
   }  

   static class FactorialService implements Callable<Long> {
      private int number;

      public FactorialService(int number) {
         this.number = number;
      }

      @Override
      public Long call() throws Exception {
         return factorial();
      }

      private Long factorial() throws InterruptedException {
         long result = 1; 
         
         while (number != 0) { 
            result = number * result; 
            number--; 
            Thread.sleep(100); 
         }
         return result;	
      }
   }
}

Cela produira le résultat suivant.

Production

Factorial Service called for 10!
Factorial Service called for 20!
10! = 3628800
20! = 2432902008176640000

Le framework fork-join permet de casser une certaine tâche sur plusieurs ouvriers puis d'attendre le résultat pour les combiner. Il exploite dans une large mesure la capacité des machines multiprocesseurs. Voici les concepts et objets de base utilisés dans le framework fork-join.

Fourchette

Fork est un processus dans lequel une tâche se divise en sous-tâches plus petites et indépendantes qui peuvent être exécutées simultanément.

Syntaxe

Sum left  = new Sum(array, low, mid);
left.fork();

Ici, Sum est une sous-classe de RecursiveTask et left.fork () transforme la tâche en sous-tâches.

Joindre

Join est un processus dans lequel une tâche joint tous les résultats des sous-tâches une fois que les sous-tâches ont fini de s'exécuter, sinon elle continue d'attendre.

Syntaxe

left.join();

Voici à gauche un objet de la classe Sum.

FourcheJoinPiscine

il s'agit d'un pool de threads spécial conçu pour fonctionner avec le fractionnement des tâches de type fork-and-join.

Syntaxe

ForkJoinPool forkJoinPool = new ForkJoinPool(4);

Voici un nouveau ForkJoinPool avec un niveau de parallélisme de 4 CPU.

RecursiveAction

RecursiveAction représente une tâche qui ne renvoie aucune valeur.

Syntaxe

class Writer extends RecursiveAction {
   @Override
   protected void compute() { }
}

Tâche récursive

RecursiveTask représente une tâche qui renvoie une valeur.

Syntaxe

class Sum extends RecursiveTask<Long> {
   @Override
   protected Long compute() { return null; }
}

Exemple

Le programme TestThread suivant montre l'utilisation du framework Fork-Join dans un environnement basé sur les threads.

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException, 
      ExecutionException {
      
      int nThreads = Runtime.getRuntime().availableProcessors();
      System.out.println(nThreads);
      
      int[] numbers = new int[1000]; 

      for(int i = 0; i < numbers.length; i++) {
         numbers[i] = i;
      }

      ForkJoinPool forkJoinPool = new ForkJoinPool(nThreads);
      Long result = forkJoinPool.invoke(new Sum(numbers,0,numbers.length));
      System.out.println(result);
   }  

   static class Sum extends RecursiveTask<Long> {
      int low;
      int high;
      int[] array;

      Sum(int[] array, int low, int high) {
         this.array = array;
         this.low   = low;
         this.high  = high;
      }

      protected Long compute() {
         
         if(high - low <= 10) {
            long sum = 0;
            
            for(int i = low; i < high; ++i) 
               sum += array[i];
               return sum;
         } else {	    	
            int mid = low + (high - low) / 2;
            Sum left  = new Sum(array, low, mid);
            Sum right = new Sum(array, mid, high);
            left.fork();
            long rightResult = right.compute();
            long leftResult  = left.join();
            return leftResult + rightResult;
         }
      }
   }
}

Cela produira le résultat suivant.

Production

32
499500

Une interface java.util.concurrent.BlockingQueue est une sous-interface de l'interface de file d'attente et prend en charge en outre des opérations telles que l'attente que la file d'attente devienne non vide avant de récupérer un élément, et attendre que de l'espace devienne disponible dans la file d'attente avant de stocker un élément .

Méthodes BlockingQueue

Sr.No. Méthode et description
1

boolean add(E e)

Insère l'élément spécifié dans cette file d'attente s'il est possible de le faire immédiatement sans violer les restrictions de capacité, en retournant true en cas de succès et en lançant une IllegalStateException si aucun espace n'est actuellement disponible.

2

boolean contains(Object o)

Renvoie true si cette file d'attente contient l'élément spécifié.

3

int drainTo(Collection<? super E> c)

Supprime tous les éléments disponibles de cette file d'attente et les ajoute à la collection donnée.

4

int drainTo(Collection<? super E> c, int maxElements)

Supprime au maximum le nombre donné d'éléments disponibles de cette file d'attente et les ajoute à la collection donnée.

5

boolean offer(E e)

Insère l'élément spécifié dans cette file d'attente s'il est possible de le faire immédiatement sans violer les restrictions de capacité, renvoyant true en cas de succès et false si aucun espace n'est actuellement disponible.

6

boolean offer(E e, long timeout, TimeUnit unit)

Insère l'élément spécifié dans cette file d'attente, en attendant le temps d'attente spécifié si nécessaire pour que l'espace devienne disponible.

sept

E poll(long timeout, TimeUnit unit)

Récupère et supprime la tête de cette file d'attente, en attendant le temps d'attente spécifié si nécessaire pour qu'un élément devienne disponible.

8

void put(E e)

Insère l'élément spécifié dans cette file d'attente, en attendant si nécessaire que de l'espace devienne disponible.

9

int remainingCapacity()

Renvoie le nombre d'éléments supplémentaires que cette file d'attente peut idéalement (en l'absence de contraintes de mémoire ou de ressources) accepter sans blocage, ou Integer.MAX_VALUE s'il n'y a pas de limite intrinsèque.

dix

boolean remove(Object o)

Supprime une seule instance de l'élément spécifié de cette file d'attente, si elle est présente.

11

E take()

Récupère et supprime la tête de cette file d'attente, en attendant si nécessaire qu'un élément devienne disponible.

Exemple

Le programme TestThread suivant montre l'utilisation de l'interface BlockingQueue dans un environnement basé sur les threads.

import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(10);

      Producer producer = new Producer(queue);
      Consumer consumer = new Consumer(queue);

      new Thread(producer).start();
      new Thread(consumer).start();

      Thread.sleep(4000);
   }  


   static class Producer implements Runnable {
      private BlockingQueue<Integer> queue;

      public Producer(BlockingQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         Random random = new Random();

         try {
            int result = random.nextInt(100);
            Thread.sleep(1000);
            queue.put(result);
            System.out.println("Added: " + result);
            
            result = random.nextInt(100);
            Thread.sleep(1000);
            queue.put(result);
            System.out.println("Added: " + result);
            
            result = random.nextInt(100);
            Thread.sleep(1000);
            queue.put(result);
            System.out.println("Added: " + result);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }	   
   }

   static class Consumer implements Runnable {
      private BlockingQueue<Integer> queue;

      public Consumer(BlockingQueue queue) {
         this.queue = queue;
      }
      
      @Override
      public void run() {
         
         try {
            System.out.println("Removed: " + queue.take());
            System.out.println("Removed: " + queue.take());
            System.out.println("Removed: " + queue.take());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Cela produira le résultat suivant.

Production

Added: 52
Removed: 52
Added: 70
Removed: 70
Added: 27
Removed: 27

Une interface java.util.concurrent.ConcurrentMap est une sous-interface de l'interface Map, prend en charge les opérations atomiques sur la variable de carte sous-jacente. Il a des méthodes get et set qui fonctionnent comme des lectures et des écritures sur des variables volatiles. Autrement dit, un ensemble a une relation qui se produit avant avec tout get ultérieur sur la même variable. Cette interface garantit la sécurité des fils et les garanties d'atomicité.

Méthodes ConcurrentMap

Sr.No. Méthode et description
1

default V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

Tente de calculer un mappage pour la clé spécifiée et sa valeur mappée actuelle (ou null s'il n'y a pas de mappage actuel).

2

default V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)

Si la clé spécifiée n'est pas déjà associée à une valeur (ou est mappée à null), tente de calculer sa valeur à l'aide de la fonction de mappage donnée et la saisit dans cette mappe à moins qu'elle ne soit nulle.

3

default V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

Si la valeur de la clé spécifiée est présente et non nulle, tente de calculer un nouveau mappage en fonction de la clé et de sa valeur mappée actuelle.

4

default void forEach(BiConsumer<? super K,? super V> action)

Exécute l'action donnée pour chaque entrée de cette mappe jusqu'à ce que toutes les entrées aient été traitées ou que l'action lève une exception.

5

default V getOrDefault(Object key, V defaultValue)

Renvoie la valeur à laquelle la clé spécifiée est mappée, ou defaultValue si cette mappe ne contient aucun mappage pour la clé.

6

default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)

Si la clé spécifiée n'est pas déjà associée à une valeur ou est associée à null, l'associe à la valeur non nulle donnée.

sept

V putIfAbsent(K key, V value)

Si la clé spécifiée n'est pas déjà associée à une valeur, associez-la à la valeur donnée.

8

boolean remove(Object key, Object value)

Supprime l'entrée d'une clé uniquement si elle est actuellement mappée à une valeur donnée.

9

V replace(K key, V value)

Remplace l'entrée d'une clé uniquement si elle est actuellement mappée à une valeur.

dix

boolean replace(K key, V oldValue, V newValue)

Remplace l'entrée d'une clé uniquement si elle est actuellement mappée à une valeur donnée.

11

default void replaceAll(BiFunction<? super K,? super V,? extends V> function)

Remplace la valeur de chaque entrée par le résultat de l'appel de la fonction donnée sur cette entrée jusqu'à ce que toutes les entrées aient été traitées ou que la fonction lève une exception.

Exemple

Le programme TestThread suivant montre l'utilisation de ConcurrentMap vs HashMap.

import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class TestThread {

   public static void main(final String[] arguments) {
      Map<String,String> map = new ConcurrentHashMap<String, String>();

      map.put("1", "One");
      map.put("2", "Two");
      map.put("3", "Three");
      map.put("5", "Five");
      map.put("6", "Six");

      System.out.println("Initial ConcurrentHashMap: " + map);
      Iterator<String> iterator = map.keySet().iterator();

      try { 
         
         while(iterator.hasNext()) {
            String key = iterator.next();
            
            if(key.equals("3")) {
               map.put("4", "Four");
            }
         }
      } catch(ConcurrentModificationException cme) {
         cme.printStackTrace();
      }
      System.out.println("ConcurrentHashMap after modification: " + map);

      map = new HashMap<String, String>();

      map.put("1", "One");
      map.put("2", "Two");
      map.put("3", "Three");
      map.put("5", "Five");
      map.put("6", "Six");

      System.out.println("Initial HashMap: " + map);
      iterator = map.keySet().iterator();

      try {
         
         while(iterator.hasNext()) {
            String key = iterator.next();
            
            if(key.equals("3")) {
               map.put("4", "Four");
            }
         }
         System.out.println("HashMap after modification: " + map);
      } catch(ConcurrentModificationException cme) {
         cme.printStackTrace();
      }
   }  
}

Cela produira le résultat suivant.

Production

Initial ConcurrentHashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
ConcurrentHashMap after modification: {1 = One, 2 = Two, 3 = Three, 4 = Four, 5 = Five, 6 = Six}
Initial HashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextNode(Unknown Source)
	at java.util.HashMap$KeyIterator.next(Unknown Source)
	at TestThread.main(TestThread.java:48)

Une interface java.util.concurrent.ConcurrentNavigableMap est une sous-interface de l'interface ConcurrentMap et prend en charge les opérations NavigableMap, et de manière récursive pour ses sous-cartes navigables et les correspondances approximatives.

Méthodes ConcurrentMap

Sr.No. Méthode et description
1

NavigableSet<K> descendingKeySet()

Renvoie une vue NavigableSet en ordre inverse des clés contenues dans cette carte.

2

ConcurrentNavigableMap<K,V> descendingMap()

Renvoie une vue en ordre inverse des mappages contenus dans cette carte.

3

ConcurrentNavigableMap<K,V> headMap(K toKey)

Renvoie une vue de la partie de cette carte dont les clés sont strictement inférieures à toKey.

4

ConcurrentNavigableMap<K,V> headMap(K toKey, boolean inclusive)

Renvoie une vue de la partie de cette carte dont les clés sont inférieures (ou égales à, si inclusive est vraie) toKey.

5

NavigableSet<K> keySet()

Renvoie une vue NavigableSet des clés contenues dans cette carte.

6

NavigableSet<K> navigableKeySet()

Renvoie une vue NavigableSet des clés contenues dans cette carte.

sept

ConcurrentNavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)

Renvoie une vue de la partie de cette carte dont les clés vont de fromKey à toKey.

8

ConcurrentNavigableMap<K,V> subMap(K fromKey, K toKey)

Renvoie une vue de la partie de cette carte dont les clés vont de fromKey, inclus, à toKey, exclusif.

9

ConcurrentNavigableMap<K,V> tailMap(K fromKey)

Renvoie une vue de la partie de cette carte dont les clés sont supérieures ou égales à fromKey.

dix

ConcurrentNavigableMap<K,V> tailMap(K fromKey, boolean inclusive)

Renvoie une vue de la partie de cette carte dont les clés sont supérieures (ou égales à, si inclusive est vraie) fromKey.

Exemple

Le programme TestThread suivant montre l'utilisation de ConcurrentNavigableMap.

import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;

public class TestThread {

   public static void main(final String[] arguments) {
      ConcurrentNavigableMap<String,String> map =
         new ConcurrentSkipListMap<String, String>();

      map.put("1", "One");
      map.put("2", "Two");
      map.put("3", "Three");
      map.put("5", "Five");
      map.put("6", "Six");

      System.out.println("Initial ConcurrentHashMap: "+map);
      System.out.println("HeadMap(\"2\") of ConcurrentHashMap: "+map.headMap("2"));
      System.out.println("TailMap(\"2\") of ConcurrentHashMap: "+map.tailMap("2"));
      System.out.println(
         "SubMap(\"2\", \"4\") of ConcurrentHashMap: "+map.subMap("2","4"));
   }  
}

Cela produira le résultat suivant.

Production

Initial ConcurrentHashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
HeadMap("2") of ConcurrentHashMap: {1 = One}
TailMap("2") of ConcurrentHashMap: {2 = Two, 3 = Three, 5 = Five, 6 = Six}
SubMap("2", "4") of ConcurrentHashMap: {2 = Two, 3 = Three}