2013-10-05 4 views
0

J'ai créé une calculatrice très simpel à des fins d'apprentissage où je veux qu'un "annonceur" annonce le résultat de l'opération dans un JTextArea. Le but de ce programme est d'utiliser les choses que nous avons apprises à l'université. J'ai une propre classe d'annonce qui implémente runnable et synchronise mon objet calc et c'est supposé ajouter le résultat mais rien ne se passe.La synchronisation ne fonctionne pas

Le programme fonctionne bien donc j'ai dépouillé le code pour les parties pertinentes:

La méthode d'exécution dans la classe annonceur:

public void run() 
{ 
    calc.getAnnouncementJTF().append("Announcer thread has been initialized...\n"); 
    calc.getAnnouncementJTF().append("Hi. My name is " + this.getName() + " and my purpose is to " + 
      "announce the results of your operations as soon as they are ready. \r\n"); 
    while(true){ 
     synchronized(calc){ 
      try 
      { 
       calc.wait(); 
      } 
      catch(InterruptedException e) 
      { 
       e.printStackTrace(); 
      } 
     } 
     calc.getAnnouncementJTF().append(String.valueOf(calc.getResult() + "\n")); 
    } 

} 

Et est ici le conditionnel qui effectuera le calcul et informer tous les objets en attente:

  else if(e.getSource() == sumButton && Numbers.size() != 0) 
     { 
      if(!actualNumber.getText().equals("")){ 
       Numbers.add(new Double(actualNumber.getText())); 
       addToResults = previousNumbers.getText() + " " + actualNumber.getText(); 
      } else { 
       addToResults = previousNumbers.getText().substring(0, previousNumbers.getText().length() - 2); 
      } 
      currentResult = performCalculation(Numbers, Operators); 
      previousCalcs.append(addToResults + " = " + String.valueOf(currentResult) + "\r\n"); 
      // Clear both lists and add the result to a cleared 
      // list for further calculation on it. 
      Operators.clear(); 
      Numbers.clear(); 
      actualNumber.setText(String.valueOf(currentResult)); 
      previousNumbers.setText(""); 
      resultExist = true; 
      synchronized(this){ 
       notifyAll(); 
      } 
     } 

méthode principale

public static void main(String[] args){ 
    Announcer announcer = new Announcer(); 
    Calculator calc = new Calculator(announcer); 

    calc.setTitle("Calculator"); 
    calc.setSize(360, 900); 
    calc.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    calc.setLocationRelativeTo(null); 
    calc.setVisible(true); 
    calc.setResizable(false); 

    announcer.setCalculator(calc); announcer.setName("Sam"); 
    announcer.setAge(28); announcer.setLength(182.5); 

    Thread announcerThread = new Thread(announcer); 
    announcerThread.start(); 
} 

Comme je l'ai mentionné avant le code fonctionne bien. Le seul problème que j'ai est que cette partie de la méthode d'exécution en classe annonceur est pas exécuté:

calc.getAnnouncementJTF().append(String.valueOf(calc.getResult() + "\n")); 
+2

Vous avez des problèmes de synchronisation dans votre classe, mais vous avez également un gros problème de Swing: les composants Swing ne sont pas accessibles à partir d'un thread autre que l'EDT. Lisez http://docs.oracle.com/javase/tutorial/uiswing/concurrency/ –

+0

Déplacez 'calc.getAnnouncementJTF(). Append()' dans le bloc 'synchronized'. Et, comme @JBNizet l'a déclaré, nous 'SwingUtilities.invokeAndWait()' pour mettre à jour le texte en dehors de l'EDT. Où est appelée la "condition"? – Matthieu

Répondre

0

Comme nous l'avons souligné dans les commentaires, le code de modifier l'état de l'interface utilisateur doit être exécuté dans le thread de répartition des événements. Mais je suppose que vous faites déjà la mise à jour dans l'EDT. Dans ce cas, le problème est que vous exécutez une interrogation de boucle infinie pour de nouveaux résultats et que vous les ajoutez au JTextField. Cela fonctionne, mais vous bloquez le thread d'envoi d'événement avec votre boucle infinie afin qu'il n'ait aucune chance de repeindre la zone de l'interface utilisateur affectée.

Une solution simple consiste à laisser le thread de calcul planifier Runnable s en utilisant EventQueue.invokeLater qui effectuera une seule mise à jour et retour. Une autre façon est de laisser un Swing Timer exécuter un Runnable périodiquement qui se synchronisera sur l'objet calc et demandera le résultat actuel. Notez que dans le dernier cas, comme dans le code que vous avez maintenant, vous pourriez manquer des mises à jour car notifyAll n'est pas garanti d'être jumelé avec wait appels d'autres discussions.

De plus, il ne suffit pas de se synchroniser uniquement pour les appels wait et notify. Le code qui modifie le résultat et le code qui récupère le résultat (l'invocation calc.getResult()) doit également se trouver dans le bloc synchronized.

Questions connexes