11

Ho un'applicazione swing che memorizza un elenco di oggetti. Quando gli utenti fanno clic su un pulsante,Prevent Swing GUI che si blocca durante un'attività in background

Voglio eseguire due operazioni su ciascun oggetto nell'elenco, e una volta completato, grafico i risultati in un JPanel. Ho provato SwingWorker, Callable & Eseguibile per l'elaborazione, ma non importa cosa faccio, mentre elabora l'elenco (che può richiedere alcuni minuti, dato che è legato all'IO), la GUI è bloccata.

Ho la sensazione che sia probabilmente il modo in cui sto chiamando i thread o qualcosa del genere, o potrebbe avere a che fare con la funzione grafica? Questo non è threaded in quanto è molto veloce.

Devo fare anche le due fasi di elaborazione, quindi qual è il modo migliore per assicurarsi che il secondo abbia aspettato il primo? Ho usato join(), e poi

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

per cercare di garantire questo, ma io sono preoccupato che questo potrebbe essere la causa del mio problema troppo.

Ho cercato dappertutto alcune indicazioni, ma poiché non riesco a trovarne sono sicuro che sto facendo qualcosa di stupido qui.

+2

Potrebbe essere utile includere un codice che mostri come stai usando SwingWorker. –

+0

concordato; mostraci il codice problematico. – Rob

risposta

18

Il problema è che l'attività a esecuzione prolungata sta bloccando il thread che mantiene la GUI reattiva.

Quello che dovrete fare è mettere l'attività di lunga durata su un altro thread.

Alcuni modi comuni per farlo sono l'uso di timer o di SwingWorker.

Il Java tutorials ha molte informazioni riguardo a queste cose nella loro lezione di concorrenza.

Per assicurarsi che la prima attività termini prima del secondo, è sufficiente metterli entrambi sullo stesso thread. In questo modo non dovrai preoccuparti di mantenere correttamente sincronizzati due diversi thread.

Ecco un esempio di implementazione di uno SwingWorkerFor vostro caso:

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. 
    } 
} 

Per utilizzare questo codice, quando l'evento per modificare la lista è sparato, creare un nuovo SwingWorker e dirgli di iniziare.

+1

Sono abbastanza sicuro che la domanda abbia già parlato di thread e SwingWorker. Inoltre, SwingUtilities.invokeLater() esegue le cose sul thread della GUI. – Powerlord

+0

Sì, ho dato un po 'di risposta generica al threading swing ... kinda sucks – jjnguy

+0

OP qui, Sì, ho già provato entrambi i suggerimenti, ma forse non li ho implementati correttamente ... Ho passato il tutorial sull'oscillazione , ma non ha molto sull'altopiano, almeno non le informazioni che sto cercando – Simonw

5

Non stanno tornando il filo oscillazione correttamente. Mi rendo conto che stai usando callable/eseguibile, ma suppongo che tu non stia facendo bene (anche se non hai inserito abbastanza codice per saperlo con certezza).

La struttura di base sarebbe:

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 
    } 
} 

questa è solo la parte superiore della mia testa, ma credo che si sia non stanno facendo il Thread.start e sono invece chiamando il metodo run direttamente, o stai facendo qualcos'altro nel primo metodo che lo blocca (come thread.join). Nessuno di questi avrebbe liberato il filo dell'oscillazione. Il primo metodo DEVE tornare rapidamente, il metodo run() può impiegare tutto il tempo che vuole.

Se si sta eseguendo un thread.join nel primo metodo, il thread NON viene restituito al sistema!

Modifica: (Seconda modifica in realtà) Penso di parlare del problema che si sta effettivamente provando - si potrebbe voler pensare di più in termini di un sistema modello/vista/controllore. Il codice che stai scrivendo è il controller (la vista è generalmente considerata come i componenti sullo schermo - la vista/il controller sono in genere strettamente vincolati).

Quando il controller riceve l'evento, dovrebbe passare il lavoro al modello. La vista è quindi fuori dall'immagine. Non aspetta il modello, è appena finito.

Quando il modello è finito, è necessario quindi dire al controller di fare qualcos'altro. Lo fa attraverso uno dei metodi di richiamo. Questo trasferisce il controllo al controller e si procede in modo allegro. Se ci pensi in questo modo, separare il controllo e passarlo deliberatamente avanti e indietro non sembra così ingombrante, e in realtà è molto comune farlo in questo modo.

+0

Nel mio caso stavo facendo thread.run() invece di thread.start(), e questo mi ha portato al blocco della GUI. L'impostazione di thread.start() risolve il problema. – Alexey

1

Sembra che il problema sia che si sta aspettando che i thread finiscano all'interno del thread della GUI. Il tuo thread GUI non dovrebbe aspettare su questi thread, invece dovresti fare in modo che i thread worker invochino un metodo sul thread della GUI che imposta un flag. Quando entrambi i flag sono impostati, sai che entrambi i thread sono finiti e puoi fare il grafico.

0

non posso davvero parlare con il modello di threading swing, ma:

devo fare le due fasi di lavorazione al fine troppo, quindi qual è il modo migliore per garantire il secondo ha aspettato su il primo?

Per questo tipo di funzionalità, suggerisco di creare due thread di lavoro e incorporare un broker JMS. Consegnare il lavoro ai due thread passando i messaggi nelle code JMS da cui hanno letto. Il thread GUI è libero di esaminare le code per determinare quando il lavoro sta accadendo e rappresentare lo stato di avanzamento dell'interfaccia utente.

0

La soluzione al mio problema era una miscela di jjnguy e le risposte di Bill K, quindi grazie mille per quei ragazzi. Avevo bisogno di utilizzare i thread all'interno di uno SwingWorker come questo:

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(); 
} 

Questo fatto in modo che tutto il lavoro è stato svolto dai thread di lavoro lontano dalla GUI, e anche garantire che lo SwingWorker in sé non stava facendo tutto il lavoro , che potrebbe essere stato un problema.

+3

Whoa whoa whoa! Perché stai generando un thread da SwingWorker? SwingWorkers (quando avviato con il metodo execute()) non viene eseguito sul thread della GUI, quindi non ci dovrebbero essere motivi per creare un altro thread qui. –

+0

Inizialmente dovevo aver sbagliato, perché quella era l'unica soluzione che funzionava per me – Simonw