Partage
  • Partager sur Facebook
  • Partager sur Twitter

Java Thread : comment bien synchroniser un compteu

Comment faire un décompte multitread par plusieurs thread

Sujet résolu
    2 février 2023 à 16:33:44

    Bonjour,

    j'ai écrit le code suivant :

    public class Counter {
        public static void main(String[] args) {
            CountDown countDown = new CountDown(30);
            CountDownThread cdt1 = new CountDownThread(countDown);
            cdt1.setName("Thread 1");
            CountDownThread cdt2 = new CountDownThread(countDown);
            cdt2.setName("Thread 2");
            CountDownThread cdt3 = new CountDownThread(countDown);
            cdt3.setName("Thread 3");
    
            cdt1.start();
            cdt2.start();
            cdt3.start();
        }
    }
    
    class CountDown {
        private int i;
    
        public CountDown(int i) {
            this.i = i;
        }
    
        public void countDown() {
            while (i > 0) {
                System.out.println(Thread.currentThread().getName() + " : i = " + i);
                i--;
            }
    
        }
    }
    
    class CountDownThread extends Thread {
        private CountDown counter;
    
        public CountDownThread(CountDown counter) {
            this.counter = counter;
        }
    
        @Override
        public void run() {
            this.counter.countDown();
        }
    }
    

    Et en output j'ai ce qui suit :

    Thread 3 : i = 30
    Thread 3 : i = 29
    Thread 3 : i = 28
    Thread 2 : i = 30
    Thread 2 : i = 26
    Thread 2 : i = 25
    Thread 1 : i = 30
    Thread 1 : i = 23
    Thread 1 : i = 22
    Thread 2 : i = 24
    Thread 2 : i = 20
    Thread 2 : i = 19
    Thread 2 : i = 18
    Thread 2 : i = 17
    Thread 3 : i = 27
    Thread 3 : i = 15
    Thread 3 : i = 14
    Thread 3 : i = 13
    Thread 3 : i = 12
    Thread 2 : i = 16
    Thread 2 : i = 10
    Thread 2 : i = 9
    Thread 2 : i = 8
    Thread 1 : i = 21
    Thread 2 : i = 7
    Thread 3 : i = 11
    Thread 2 : i = 5
    Thread 2 : i = 3
    Thread 2 : i = 2
    Thread 1 : i = 6
    Thread 2 : i = 1
    Thread 3 : i = 4

    Y a t il y une façon de synchroniser la classe CountDown pour que tous les threads ne comptent pas le le nombre «30» plusieurs fois ?

    • Partager sur Facebook
    • Partager sur Twitter
      20 février 2023 à 10:54:22

      On peut synchroniser une méthode qui agit sur la variable partagée

      class CountDown {
          private int i;
       
          public CountDown(int i) {
              this.i = i;
          }
      
      	synchronized boolean decrement() {
      		if (i > 0) {
      			System.out.println(Thread.currentThread().getName()
      							   + " : i = " + i);
                  i--;
      			return true;
              }
      		else return false;
      	}
      		
      	public void countDown() {
      		
              while (decrement()) {
      	}   
       
          }
      }
      $ java Counter
      Thread 1 : i = 30
      Thread 1 : i = 29
      Thread 1 : i = 28
      Thread 1 : i = 27
      Thread 1 : i = 26
      Thread 3 : i = 25
      Thread 3 : i = 24
      Thread 3 : i = 23
      Thread 3 : i = 22
      Thread 3 : i = 21
      Thread 3 : i = 20
      Thread 3 : i = 19
      Thread 3 : i = 18
      Thread 3 : i = 17
      Thread 3 : i = 16
      Thread 3 : i = 15
      Thread 3 : i = 14
      Thread 3 : i = 13
      Thread 3 : i = 12
      Thread 3 : i = 11
      Thread 3 : i = 10
      Thread 3 : i = 9
      Thread 3 : i = 8
      Thread 3 : i = 7
      Thread 3 : i = 6
      Thread 3 : i = 5
      Thread 3 : i = 4
      Thread 3 : i = 3
      Thread 3 : i = 2
      Thread 3 : i = 1
      


      PS: si on synchronise countDown, un seul compteur agira sur la variable, de 30 à 1

      -
      Edité par michelbillaud 20 février 2023 à 10:56:04

      • Partager sur Facebook
      • Partager sur Twitter
        18 juin 2023 à 19:21:02

        C'est très clair. Mais le seul problème c'est qu'un seul thread fait le décompte.

        -
        Edité par jeansimplicepat 18 juin 2023 à 19:35:57

        • Partager sur Facebook
        • Partager sur Twitter
          18 juin 2023 à 22:01:33

          > c'est qu'un seul thread fait le décompte.

          C'est bien de s'y intéresser encore 4 mois après.

          Un seul thread : je ne pense pas non.  C'est d'ailleurs marqué dans les résultats que j'ai montrés et que je n'ai pas bidonnés

          Thread 1 : i = 26
          Thread 3 : i = 25
          


          Mais avec 30 tours, il y tellement peu de choses à faire que ça n'a pas le temps de switcher d'un thread à l'autre. Faut avoir de la chance, ou alors mettre des délais.

          Essai avec

          	public void countDown() {
          		
                  while (decrement()) {
                      try {
          			Thread.sleep(100);
                      } catch(InterruptedException e) {
                          e.printStackTrace();
                      }
          		}   
           
              }

          Exécution

          Thread 1 : i = 30
          Thread 3 : i = 29
          Thread 2 : i = 28
          Thread 1 : i = 27
          Thread 3 : i = 26
          Thread 2 : i = 25
          Thread 1 : i = 24
          etc

          Mais bon, à mon avis il y a un défaut structurel dans ton programme. D'abord les classes sont nommées n'importe comment.

          1. Appelons la classe Principale "DemoSynchro" pour garder le nom Counter pour un truc qui contient un compteur

          2.  Le compteur, c'est pas son job de faire des boucles. Lui il essaie de se décrémenter si on lui demande, c'est tout.

          class Counter
          {
              private int i;
          
              public Counter(int i)
              {
                  this.i = i;
              }
          
              synchronized boolean decrement()
              {
                  if (i > 0) {
                      System.out.println(Thread.currentThread().getName()
                                         + " : i = " + i);
                      i--;
                      return true;
                  }
                  return false;
              }
          }

          3. C'est le thread qui fait la boucle (dans son run)

          class CountDownThread extends Thread
          {
              private Counter counter;
          
              public CountDownThread(Counter counter)
              {
                  this.counter = counter;
              }
          
              @Override
              public
              void run()
              {
                  while (counter.decrement()) {
          
                      try {
                          Thread.sleep(100);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
          
                  }
              }
          }

          4. Et la classe principale, elle lance des threads, et  n'oublie pas d'attendre ensuite qu'ils aient fini

          public class DemoSyncho
          {
              static final int MAX  = 30;
          
              public static void main(String[] args) throws InterruptedException {
                  Counter counter = new Counter(MAX);
                  CountDownThread t1 = new CountDownThread(counter);
                  t1.setName("Thread 1");
                  CountDownThread t2 = new CountDownThread(counter);
                  t2.setName("Thread 2");
                  CountDownThread t3 = new CountDownThread(counter);
                  t3.setName("Thread 3");
          
                  t1.start();
                  t2.start();
                  t3.start();
                  // et maintenant on attend la fin des threads
                  t1.join();
                  t2.join();
                  t3.join();
          
              }
          }


          ----------

          Une autre version, en remarquant que

          • le rôle d'un compteur est de fournir une séquence de nombres, à la demande. Pas de les afficher
          • quand on lui demande, y en a encore ou y en a plus, c'est une bonne raison d'employer  des Optional, enfin OptionalInt
          • On  est dans la 3ieme décennie après l'an 2000, alors il est temps de ce décider à écrire du java un peu moderne avec des var, plutôt que des déclarations de types redondantes.
          import java.util.OptionalInt;
          
          public class DemoSyncho
          {
              static final int MAX  = 30;
          
              public static void main(String[] args) throws InterruptedException {
                  var sequence = new CountdownSequence(MAX);
                  var t1 = new CountDownThread(sequence);
                  t1.setName("Thread 1");
                  var t2 = new CountDownThread(sequence);
                  t2.setName("Thread 2");
                  var t3 = new CountDownThread(sequence);
                  t3.setName("Thread 3");
          
                  t1.start();
                  t2.start();
                  t3.start();
          
                  t1.join();
                  t2.join();
                  t3.join();
          
              }
          }
          
          class CountdownSequence
          {
              private int next;
          
              public CountdownSequence(int start)
              {
                  next = start;
              }
          
              synchronized OptionalInt getNext()
              {
                  if (next == 0) {
                      return OptionalInt.empty();
                  } else {
                      return OptionalInt.of(next--);
                  }
              }
          
          }
          
          class CountDownThread extends Thread
          {
              private final CountdownSequence sequence;
          
              public CountDownThread(CountdownSequence sequence)
              {
                  this.sequence = sequence;
              }
          
              @Override
              public
              void run()
              {
                  while(true) {
                      var next = sequence.getNext();
                      if (next.isPresent()) {
                          System.out.format("%s -> %d\n", getName(), next.getAsInt());
                          try {
                              Thread.sleep(100);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      } else {
                          break;
                      }
                  }
              }
          }
          


          Résultat

          Thread 1 -> 30
          Thread 3 -> 28
          Thread 2 -> 29
          Thread 2 -> 27
          Thread 3 -> 26
          Thread 1 -> 25
          Thread 2 -> 24
          Thread 3 -> 23
          Thread 1 -> 22
          Thread 2 -> 21
          Thread 3 -> 20
          Thread 1 -> 19
          Thread 2 -> 18
          Thread 1 -> 16
          Thread 3 -> 17
          Thread 2 -> 15
          Thread 1 -> 14
          Thread 3 -> 13
          Thread 2 -> 12
          Thread 1 -> 11
          Thread 3 -> 10
          Thread 2 -> 9
          Thread 1 -> 8
          Thread 3 -> 7
          Thread 2 -> 6
          Thread 1 -> 5
          Thread 3 -> 4
          Thread 2 -> 3
          Thread 1 -> 2
          Thread 3 -> 1



          -
          Edité par michelbillaud 19 juin 2023 à 13:08:41

          • Partager sur Facebook
          • Partager sur Twitter
            21 juin 2023 à 21:37:57

            C'est très clair. Mais on a des points de désaccord. var n'est pas à utiliser systématiquement. L'usage de Optional est un petit peux forcé dans la version 2, il n'y a pas de risque que la séquence soit nulle. Pas besoin d'attendre la fin du thread sans raison.

            • Partager sur Facebook
            • Partager sur Twitter
              21 juin 2023 à 22:27:53

              C'est pas la séquence qui est nulle, c'est la réponse à la requête "file moi le suivant de la sequence", quand il n'y en n'a plus.

              • Partager sur Facebook
              • Partager sur Twitter

              Java Thread : comment bien synchroniser un compteu

              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
              • Editeur
              • Markdown