Java - Synchronisation des threads

Lorsque nous démarrons deux ou plusieurs threads dans un programme, il peut y avoir une situation où plusieurs threads essaient d'accéder à la même ressource et finalement ils peuvent produire des résultats imprévus en raison de problèmes de concurrence. Par exemple, si plusieurs threads essaient d'écrire dans un même fichier, ils peuvent corrompre les données car l'un des threads peut remplacer les données ou pendant qu'un thread ouvre le même fichier en même temps, un autre thread peut fermer le même fichier.

Il est donc nécessaire de synchroniser l'action de plusieurs threads et de s'assurer qu'un seul thread peut accéder à la ressource à un moment donné. Ceci est implémenté en utilisant un concept appelémonitors. Chaque objet en Java est associé à un moniteur, qu'un thread peut verrouiller ou déverrouiller. Un seul thread à la fois peut maintenir un verrou sur un moniteur.

Le langage de programmation Java fournit un moyen très pratique de créer des threads et de synchroniser leur tâche en utilisant synchronizedblocs. Vous conservez les ressources partagées dans ce bloc. Voici la forme générale de l'instruction synchronisée -

Syntaxe

synchronized(objectidentifier) {
   // Access shared variables and other shared resources
}

Ici le objectidentifierest une référence à un objet dont le verrou est associé au moniteur représenté par l'instruction synchronisée. Nous allons maintenant voir deux exemples, où nous allons imprimer un compteur en utilisant deux threads différents. Lorsque les threads ne sont pas synchronisés, ils impriment la valeur du compteur qui n'est pas dans l'ordre, mais lorsque nous imprimons le compteur en mettant à l'intérieur du bloc synchronized (), alors il imprime le compteur très en séquence pour les deux threads.

Exemple de multithreading sans synchronisation

Voici un exemple simple qui peut ou non imprimer la valeur du compteur en séquence et chaque fois que nous l'exécutons, il produit un résultat différent basé sur la disponibilité du processeur pour un thread.

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() {
      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 un résultat différent à chaque fois que vous exécutez ce programme -

Production

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

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.