2010-04-02 11 views
13

Mein Verständnis ist, dass, wenn ich einen anderen Thread starten, um einige Aktionen durchzuführen, ich SwingUtilities.invokeAndWait oder SwingUtilities.invokeLater benötigen würde, um die GUI zu aktualisieren, während ich in besagtem Thread bin. Bitte korrigieren Sie mich, falls ich falsch liege.Java Swing Threading

Was ich versuche zu erreichen, ist relativ einfach: Wenn der Benutzer auf Senden klickt, möchte ich (vor dem Ausführen von Aktionen) die Übermittlungsschaltfläche deaktivieren, die Aktion ausführen und am Ende der Aktion die Option erneut aktivieren Taste. Meine Methode zum Ausführen der Aktion aktualisiert die GUI direkt (zeigt Ergebnisse an), wenn sie die Ergebnisse zurückerhält.

Diese Aktion fragt grundsätzlich einen Server ab und erhält einige Ergebnisse zurück.

Was ich habe, so weit ist:

boolean isRunning = false; 

synchronized handleButtonClick() { 
    if (isRunning == false) { 
    button.setEnabled(false); 
    isRunning = true; 
    doAction(); 
    } 
} 

doAction() { 
    new Thread() { 
    try { 
     performAction(); // Concern A 
    } catch (...) { 
     displayStackTrace(...); // Concern B 
    } finally { 
     SwingUtilities.invokeLater (/* simple Runnable to enable button */); 
     isRunning = false; 
    } 
    } 
} 

Für beide meine Bedenken über, tun hätte ich SwingUtilities.invokeAndWait verwenden, da sie beide die GUI aktualisieren? Alle GUI-Updates drehen sich um die Aktualisierung JTextPane. Muss ich in meinem Thread überprüfen, ob ich auf EDT bin und wenn ja, kann ich meinen Code aufrufen (unabhängig davon, ob er die GUI aktualisiert oder nicht) und NICHT SwingUtilities.invokeAndWait verwenden?

EDIT: Hier ist, was ich jetzt tue:

handleButtonClick() { 
    if (isRunning == true) 
    return; 
    disable button; 
    SwingWorker task = new MyTask(); 
    task.execute(); 
} 

...inside MyTask 
doInBackground() { 
    return performAction(); 
} 

done() { 
    result = get(); 
    enable button; 
    isRunning = false; 
    interpret result (do most of the GUI updates here); 
} 

Während performAction() einige GUI-Updates der Fall ist, habe ich eingewickelt diejenigen in:

if (SwingUtil.isEDT()) 
    doGUIupdate() 
else 
    SwingUtil.invokeLater(new Runnable() { 
    run() { 
     doGUIupdate(); 
    } 
    }); 

Hoffentlich dies ein Schritt in die richtige Richtung Bitte kommentieren Sie, wenn Sie glauben, dass es bessere Möglichkeiten gibt, mit meiner Situation umzugehen.

+1

Verwenden Swingworker. http://java.sun.com/javase/6/docs/api/javax/swing/SwingWorker.html –

+0

Ich hatte den Eindruck, dass SwingWorker für etwas "langes" verwendet werden sollte, während SwingUtilities für etwas "schnelles" verwendet wurde. ? – nevets1219

+0

Da SwingUtilities auf dem Thread "UI" ausgeführt wird, müsste es für etwas verwendet werden, das schnell praktisch ist. Sonst könnte es die Benutzeroberfläche blockieren. Keines wird in "einem anderen Thread" ausgeführt. –

Antwort

18

Meiner Meinung nach sollten Sie fast nie invokeAndWait() verwenden. Wenn etwas dauern wird, wird Ihre Benutzeroberfläche gesperrt.

Verwenden Sie eine SwingWorker für diese Art von Sache. Werfen Sie einen Blick auf Improve Application Performance With SwingWorker in Java SE 6.

+0

Toller Link, halbwegs und es hat mir einiges gezeigt! – nevets1219

+1

invokeAndWait() blockiert die Benutzeroberfläche nicht - sie blockiert den Hilfsthread, bis die Benutzeroberfläche die übergebene Runnable ausgeführt hat. invokeAndWait() überprüft tatsächlich, um sicherzustellen, dass es nicht vom EDT aufgerufen wird (was die Sperre verursachen würde). Natürlich sollte ich sagen, dass die Verwendung von invokeAndWait() bedeutet normalerweise, dass der Programmierer nicht ordnungsgemäß ereignisgesteuerte (Beobachter) Programmierung verwendet ... –

+1

Verwendung von 'invokeAndWait' führt oft zu Deadlock. –

5

Sie sollten in Betracht ziehen, SwingWorker zu verwenden, da es den UI-Thread nicht blockiert, während beide SwingUtilities-Methoden auf dem EDT-Thread ausgeführt werden und somit die Benutzeroberfläche blockieren.

+1

SwingWorker führt die Methode done() auf dem EDT aus. Es ist das gleiche wie das Starten eines eigenen Nicht-EDT-Threads, der SwingWorker.invokeLater() aufruft, um die Benutzeroberfläche zu aktualisieren, wenn Sie fertig sind. SwingWorker ist nur ein bisschen bequemer zu benutzen als selbst zu rollen. –

+0

Guter Punkt; Ich empfehle jedoch immer 'SwingWorker' zu verwenden. –

+1

@Scott Meinen Sie SwingUtilities? – nevets1219

0

Ich halte die einfache Thread innen EventQueue.invokeLater(...) und das funktionierte reibungslos ...

java.awt.EventQueue.invokeLater(new Runnable() { 
    public void run(){ 

     new Thread(new Runnable(){ 
      public void run(){ 

       try{ 
        EdgeProgress progress = EdgeProgress.getEdgeProgress(); 
        System.out.println("now in traceProgressMonitor..."); 
        while(true){ 
         // here the swing update 
         if(monitor.getState() == ProgressMonitor.STATE_BUSY){ 
          System.out.println(monitor.getPercentDone()/2); 
          progress.setProgress(monitor.getPercentDone()/2); 
         }else{ 
          break; 
         } 
         Thread.sleep(5); 
        } 
       }catch(InterruptedException ie){} 

      } 
     }).start(); 

    } 
});