2009-06-02 14 views
11

Ich habe eine Swing-Anwendung, die eine Liste von Objekten speichert. Wenn der Benutzer auf eine Schaltfläche klickt,Prevent Swing GUI während einer Hintergrundaufgabe gesperrt

Ich möchte zwei Operationen für jedes Objekt in der Liste ausführen, und wenn das abgeschlossen ist, dann grafisch die Ergebnisse in einem JPanel. Ich habe versucht, SwingWorker, Callable & Runnable, um die Verarbeitung zu tun, aber egal was ich tue, während der Verarbeitung der Liste (die bis zu ein paar Minuten dauern kann, wie es IO gebunden ist), ist die GUI gesperrt.

Ich habe das Gefühl, es ist wahrscheinlich die Art und Weise, wie ich die Threads oder etwas anrufe, oder könnte es mit der Grafikfunktion zu tun haben? Das ist nicht gewunden, da es sehr schnell ist.

Ich muss die zwei Verarbeitungsstufen auch in Reihenfolge machen, also was ist der beste Weg, um sicherzustellen, dass der zweite auf den ersten gewartet hat? Ich habe() verwendet, verbinden, und dann

while(x.isAlive()) 
{ 
     Thread.sleep(1000); 
} 

zu versuchen und dies zu gewährleisten, aber ich mache mir Sorgen, dies auch die Ursache für mein Problem sein könnte.

Ich habe überall nach irgendwelchen Zeigern gesucht, aber da ich keine finden kann bin ich sicher, dass ich hier etwas Dummes mache.

+2

Es könnte hilfreich sein, wenn Sie Code einfügen, der Ihnen zeigt, wie Sie SwingWorker verwenden. –

+0

Einverstanden; zeig uns den problematischen Code. – Rob

Antwort

18

Das Problem ist, Ihre lange laufende Aufgabe blockiert den Thread, der die GUI reagiert hält.

Was Sie tun müssen, ist die lange laufende Aufgabe auf einen anderen Thread zu setzen.

Einige gebräuchliche Methoden hierfür sind die Verwendung von Timern oder SwingWorker.

Die Java tutorials haben viele Informationen zu diesen Dingen in ihrer Lektion in Nebenläufigkeit.

Um sicherzustellen, dass die erste Aufgabe vor der zweiten beendet wird, legen Sie sie beide auf den gleichen Thread. Auf diese Weise müssen Sie sich keine Sorgen darüber machen, dass zwei verschiedene Threads korrekt terminiert werden.

Hier ist eine Beispielimplementierung eines SwingWorkerFor Ihrem Fall:

public class YourTaskSwingWorkerSwingWorker extends SwingWorker<List<Object>, Void> { 
    private List<Object> list 
    public YourClassSwingWorker(List<Object> theOriginalList){ 
     list = theOriginalList; 
    } 

    @Override 
    public List<Object> doInBackground() { 
     // Do the first opperation on the list 
     // Do the second opperation on the list 

     return list; 
    } 

    @Override 
    public void done() { 
     // Update the GUI with the updated list. 
    } 
} 

diesen Code zu verwenden, wenn das Ereignis die Liste wird gefeuert zu ändern, erstellen Sie einen neuen SwingWorker und sagen zu starten.

+1

Ich bin mir ziemlich sicher, dass die Frage bereits Threads und SwingWorker erwähnt hat. Außerdem führt SwingUtilities.invokeLater() Dinge auf dem GUI-Thread aus. – Powerlord

+0

Ja, ich gab irgendwie 'generische Swing Threading Antwort ... irgendwie saugt – jjnguy

+0

OP hier, Yeah Ich habe beide dieser Vorschläge bereits ausprobiert, aber ich habe sie vielleicht nicht richtig implementiert ... Ich habe durch die Swing-Tutorial , aber es hat nicht viel auf Swing Worker überhaupt, zumindest nicht die Info, die ich bin – Simonw

5

Sie geben den Schwungfaden nicht ordnungsgemäß zurück. Ich weiß, dass Sie Callable/Runnable verwenden, aber ich vermute, Sie tun es nicht richtig (obwohl Sie nicht genügend Code posten, um sicher zu wissen).

würde die Grundstruktur sein:

swingMethod() { // Okay, this is a button callback, we now own the swing thread 
    Thread t=new Thread(new ActuallyDoStuff()); 
    t.start(); 
} 

public class ActuallyDoStuff() implements Runnable { 
    public void run() { 
     // this is where you actually do the work 
    } 
} 

Dies ist nur aus der Spitze von meinem Kopf, aber ich nehme an, dass Sie entweder die Thread.start nicht tun und die run-Methode statt Aufruf direkt, oder Sie tun etwas anderes in der ersten Methode, die es absperrt (wie thread.join). Keine von diesen würde den Schwungfaden freigeben. Die erste Methode MUSS schnell zurückkehren, die Methode run() kann so lange dauern, wie sie möchte.

Wenn Sie eine thread.join in der ersten Methode machen, dann wird der Thread NICHT an das System zurückgegeben!

Bearbeiten: (Zweite Bearbeitung tatsächlich) Ich denke, um das Problem zu sprechen, das Sie tatsächlich fühlen - möchten Sie vielleicht mehr in Bezug auf ein Modell/Ansicht/Controller-System denken. Der Code, den Sie schreiben, ist der Controller (die Ansicht wird allgemein als die Komponenten auf dem Bildschirm betrachtet - View/Controller sind normalerweise sehr eng gebunden).

Wenn Ihr Controller das Ereignis erhält, sollte es die Arbeit an Ihr Modell weitergeben. Die Aussicht ist dann aus dem Bild. Es wartet nicht auf das Modell, es ist einfach fertig.

Wenn Ihr Modell fertig ist, muss es dem Controller mitteilen, etwas anderes zu tun. Dies geschieht über eine der Aufrufmethoden. Dies überträgt die Kontrolle zurück an den Controller und Sie gehen auf Ihre fröhliche Art und Weise. Wenn Sie so darüber nachdenken, ist es nicht so sperrig, die Kontrolle zu trennen und absichtlich hin und her zu gehen, und es ist tatsächlich sehr üblich, dies auf diese Weise zu tun.

+0

In meinem Fall habe ich thread.run() anstelle von thread.start() ausgeführt, und das führte mich zur GUI-Sperre. Durch das Setzen von thread.start() wurde das Problem gelöst. – Alexey

1

Es klingt wie das Problem möglicherweise, dass Sie auf die Threads warten, um aus dem GUI-Thread zu beenden. Ihr GUI-Thread sollte nicht auf diese Threads warten, stattdessen sollten die Worker-Threads eine Methode im GUI-Thread aufrufen, die ein Flag setzt. Wenn beide Flags gesetzt sind, wissen Sie, dass beide Threads beendet sind und Sie können das Diagramm erstellen.

0

ich auf das Swing-Threading-Modell nicht wirklich sprechen kann, aber:

Auch ich habe die beiden Verarbeitungsstufen, um zu tun, so etwas ist der beste Weg, um die zweiten, um sicherzustellen, auf gewartet hat Der Erste?

Für diese Art von Funktionalität, würde ich vorschlagen, dass Sie zwei Worker-Threads erstellen, und einen JMS-Broker einbetten. Liefern Sie Arbeit an die zwei Threads, indem Sie Nachrichten an JMS-Warteschlangen übergeben, von denen sie gelesen haben. Ihr GUI-Thread kann die Warteschlangen untersuchen, um festzustellen, wann Arbeit stattfindet und den Status der Wiedergabe in Ihrer Benutzeroberfläche darstellen.

0

Die Lösung für mein Problem war eine Mischung aus JJNGUY und Bill K. Antworten, also vielen Dank für diese Jungs. Ich brauchte Threads innerhalb eines Swingworker wie folgt zu verwenden:

public class Worker extends SwingWorker<Void, Void> 
{ 
    private List<Object> list; 
    public YourClassSwingWorker(List<Object> theOriginalList){ 
     list = theOriginalList; 
    } 

    @Override 
    public List<Object> doInBackground() { 
     Thread t = new Thread(new ProcessorThread(list)); 
     t.start(); 
    } 

    @Override 
    public void done() { 
     // draw graph on GUI 
    } 
} 
class ProcessorThread implements Runnable { 
    //do lots of IO stuff 
    Thread t2 = new Thread(new SecondProcess()); 
    t2.start(); 
} 

Dies sorgte dafür, dass die ganze Arbeit von Arbeitsthreads von der GUI getan wurde, und gewährleistet auch, dass die Swingworker selbst war nicht die ganze Arbeit zu tun , was ein Problem sein könnte.

+3

Whoa whoa whoa! Warum spawnt du einen Thread aus dem SwingWorker? SwingWorker (wenn mit der execute() -Methode gestartet) werden nicht im GUI-Thread ausgeführt, daher sollte es keinen Grund geben, hier einen anderen Thread zu erstellen. –

+0

Ich muss es zunächst falsch gemacht haben, denn das war die einzige Lösung, die für mich funktionierte – Simonw