2011-08-01 5 views
4

Synchroniser je le code 2 classes suivantes qui produisent ce résultat par exemple:Java Threads Problème

Wainting for calculation to complete... 
Calculator thread says HELLO! 
T1 says that total is 10 
Wainting for calculation to complete... 
Wainting for calculation to complete... 

Maintenant threads attendent, mais personne ne va les informer. Comment puis-je forcer les threads de T1 à T3 à s'exécuter avant que le "thread de la calculatrice" ne s'éveille?

public class Calculator implements Runnable{ 

    private int total; 

    public int getTotal() { 
    return total; 
    } 

    @Override 
    public void run() { 
    synchronized (this) { 

     for (int i = 0; i < 5; i++) { 
      total += i; 
     } 
     System.out.println(Thread.currentThread().getName() + " says HELLO!"); 
     notifyAll(); 
    } 
    } 
} 


import static java.lang.System.out; 

public class Reader implements Runnable{ 

    private Calculator c; 


    public Reader(Calculator calc) { 
    c = calc; 
    } 

    public Calculator getCalculator() { 
    return c; 
    } 

    public static void main(String[] args) { 

    Calculator calc = new Calculator(); 
    Reader read = new Reader(calc); 

    Thread thread1 = new Thread(read); 
    Thread thread2 = new Thread(read); 
    Thread thread3 = new Thread(read); 

    thread1.setName("T1"); 
    thread2.setName("T2"); 
    thread3.setName("T3"); 

    thread1.start(); 
    thread2.start(); 
    thread3.start(); 

    Thread calcThread = new Thread(read.getCalculator()); 
    calcThread.setName("Calculator thread"); 
    calcThread.start(); 
    } 
} 


    @Override 
    public void run() { 
     synchronized (c) { 
      try { 
       out.println("Wainting for calculation to complete..."); 
       c.wait(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      out.println(Thread.currentThread().getName() + " says that " + "total is " + c.getTotal()); 
     } 

    } 

} 
+0

Je peux ajouter Thread.sleep() après le thread3.start() mais je considère que ce n'est pas une solution appropriée. – nyxz

+0

Vous ne devriez jamais utiliser wait/notify, mais plutôt utiliser les utilitaires débogués de java.util.concurrent et entièrement repenser votre application en conséquence. Ce n'est pas clair quelle est votre vraie application, donc je ne peux pas vraiment vous dire ce que vous devriez utiliser. – toto2

+0

@toto: Il n'y a aucune raison de ne pas utiliser wait/notify. Ce sont des primitives simples et elagent. Les classes java.util.concurrent ne sont que des wrappers sur le même. Je suis d'accord que des constructions de niveau supérieur telles que des verrous en lecture et en écriture, des barrières et des verrous sont offertes par ces classes. Mais vous ne devriez jamais vraiment utiliser une enclume quand un simple marteau suffirait. –

Répondre

0

Thread.Join() peut sembler une option dans cette situation particulière. Puisque vous avez le contrôle de la fonction main() et que vous savez exactement quand chaque thread démarre.

Une façon plus générale de traiter cette situation est d'utiliser une variable conditionnelle et d'appeler le c.wait() dans une boucle pour vérifier la variable de condition.

ajouter Fondamentalement, le champ isFinished dans la classe Calculator:

 
public class Calculator implements Runnable { 
... 
    public volatile boolean isFinished = false 
.. 
.. 

Ensuite, vous remplacez c.wait() avec:

 
... 
while (!c.isFinished) { 
    c.wait(); 
} 
... 

Enfin dans la méthode `run() de votre classe de calculatrice après avoir calculé le total définir le champ isFinished

 
.... 
for(int i = 0; .... 
    total = += i; 
} 
c.isFinished = true 
.... 
+0

Je vais essayer ça aussi, merci – nyxz

+0

Cela n'aide pas. – nyxz

+0

Désolé, mon mauvais! Ça aide :) C'est une belle solution simple. – nyxz

3

Voici comment j'écrirais le code. Plutôt que d'essayer de réinventer la roue avec wait/notify, j'utiliserais la bibliothèque concurrente pour faire ce qui est nécessaire, un Future.

import java.util.concurrent.*; 

public class Main { 
    static final long start = System.nanoTime(); 

    static void log(String text) { 
     double seconds = (System.nanoTime() - start)/1e9; 
     System.out.printf("%s %.6f - %s%n", Thread.currentThread().getName(), seconds, text); 
    } 

    static class Calculator implements Callable<Integer> { 
     @Override 
     public Integer call() throws Exception { 
      int total = 0; 
      log("calculating total"); 
      for (int i = 0; i < 50000; i++) 
       total += i; 
      log("total is " + total); 
      return total; 
     } 
    } 

    static class Reader implements Callable<Void> { 
     private final Future<Integer> totalFuture; 

     public Reader(Future<Integer> totalFuture) { 
      this.totalFuture = totalFuture; 
     } 

     @Override 
     public Void call() throws ExecutionException, InterruptedException { 
      log("Waiting for total."); 
      int total = totalFuture.get(); 
      log("... got total= " + total); 
      return null; 
     } 
    } 

    public static void main(String... args) { 
     ExecutorService es = Executors.newCachedThreadPool(); 
     Future<Integer> totalFuture = es.submit(new Calculator()); 
     es.submit(new Reader(totalFuture)); 
     es.submit(new Reader(totalFuture)); 
     es.submit(new Reader(totalFuture)); 
     es.shutdown(); 
    } 
} 

impressions

pool-1-thread-1 0.008154 - calculating total 
pool-1-thread-4 0.011356 - Waiting for total. 
pool-1-thread-3 0.011292 - Waiting for total. 
pool-1-thread-2 0.011128 - Waiting for total. 
pool-1-thread-1 0.025097 - total is 1249975000 
pool-1-thread-4 0.025351 - ... got total= 1249975000 
pool-1-thread-3 0.025372 - ... got total= 1249975000 
pool-1-thread-2 0.025380 - ... got total= 1249975000 

Après

thread3.start(); 

ajouter ce qui suit à attendre les fils pour terminer.

thread1.join(); 
thread2.join(); 
thread3.join(); 
+0

super! Merci, Peter! – nyxz

+0

J'ai modifié le code.J'ai effacé quelques rouleaux par erreur. Si j'ajoute ce code, les threads attendent vraiment de se terminer et le programme n'atteint jamais calcThread.start(); witch invoque la méthode notifyAll() ... Ainsi, le programme ne se termine jamais. D'autres suggestions? – nyxz

+0

Le problème est que je supprimerais tout le code qui n'est pas nécessaire jusqu'à ce que vous ayez juste une boucle pour calculer le total et une instruction d'impression. Tout le reste ne fait vraiment rien. –

0

U peut utiliser Thread.join() méthode .. Je ne sais pas comment abouts de bonnes pratiques de programmation, mais qui fonctionnera ..

+0

Voir le commentaire Je suis parti sur Peter Lawrey en utilisant la jointure() – nyxz