|
|
|
![]() |
|
Strumenti |
![]() |
#1 |
Senior Member
Iscritto dal: Jul 2002
Messaggi: 4334
|
[JAVA] Worker thread, giusto così?
Salve,
devo fare un update di un componente swing - setText o setIcon - con dei dati che impiego un po' a calcolare, e quindi devo tirare via la computazione dall'EDT, pena "freeze" della gui. C'è anche da dire che se durante l'esecuzione del worker thread ne lancio un altro voglio che sia quest'ultimo ad aggiornare il componente. In pratica le vecchie computazioni non mi interessano. Ho impostato la classe così: Codice:
public class IDTextUpdater implements Runnable { ... private static Thread lastThread; ... /* qui sono nel costruttore */ // update last thread variable lastThread = new Thread(this); lastThread.start(); /* fine costruttore */ /* nel metodo run()... */ // lavora... ... if( lastThread != Thread.currentThread() ) return; // aggiorna il componente SwingUtilities.invokeLater( new Runnable() {...} ) /* fine run() */ non si sa mai, vedete qualcosa che potrebbe non andare? Grazie.
__________________
|Java Base| |
![]() |
![]() |
![]() |
#2 |
Senior Member
Iscritto dal: Dec 2000
Città: bologna
Messaggi: 1309
|
se la computazione è grossa nel costruttore potresti mettere uno stop thread(nn ricordo se è deprecato, cmq bloccarlo in qualche maniera).
Cmq se per caso viene creato un nuovo updater, e il vecchio è bloccato dopo il controllo if( lastThread != Thread.currentThread() ), e il nuovo updater finisce prima che il controllo ritorni al vecchio updater, puoi trovarti con la textbox valorizzata col risultato del vecchio updater. |
![]() |
![]() |
![]() |
#3 |
Senior Member
Iscritto dal: Jul 2002
Messaggi: 4334
|
Grazie della risposta.
Sì, stop è deprecato, il modo pulito per terminare un thread è uscire dal metodo run(). Per il resto forse hai ragione, ma d'altra parte non saprei come fare diversamente... Forse mettendo statici i componenti da aggiornare e mettendoli a null nel costruttore? Il tutto sincronizzato ovviamente.
__________________
|Java Base| |
![]() |
![]() |
![]() |
#4 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
lastThread deve essere volatile o incapsulato in un AtomicReference.
Come indicato da thebol, il programma che hai scritto non fa quello che vorresti (eseguire solo l'ultimo aggiornamento grafico). Tra le possibili soluzioni io vedrei bene una doppia coda di consumazione di eventi Runnable. Una per le computazioni in background, una per gli aggiornamento grafici. Quando immetti una computazione, che potrebbe essere rappresentata con; Codice:
interface MultiTask { Runnable getBackgroundTask(); Runnable getAWTTask(); }
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#5 |
Senior Member
Iscritto dal: Jul 2002
Messaggi: 4334
|
Ok, ci lavoro su e vi faccio sapere.
__________________
|Java Base| |
![]() |
![]() |
![]() |
#6 |
Senior Member
Iscritto dal: Jul 2002
Messaggi: 4334
|
Allora, per i thread pool e le code bloccanti vedrò più avanti,
magari li userò per un altra cosetta che ho in mente. Intanto ho modificato il codice così: Codice:
private static IDTextUpdater lastUpdater; // tolto lastThread private boolean stopped; ... /* qui sono nel costruttore */ this.stopped = false; if( lastUpdater != null ) lastUpdater.stop(); lastUpdater = this; new Thread( this ).start(); /* fine costruttore */ void stop() { stopped = true; } /* nel metodo run()... */ // lavora... ... if( stopped ) return; SwingUtilities.invokeLater( new Runnable() { if( !stopped ) /* update componente*/...} ); /* fine run() */
__________________
|Java Base| |
![]() |
![]() |
![]() |
#7 |
Senior Member
Iscritto dal: Jul 2002
Città: Reggio Calabria -> London
Messaggi: 12112
|
e xkè non usare SwingWorker anzikè reinventare la ruota?
![]() ehm... ovviamente se epuoi usare java 6 ![]()
__________________
![]() |
![]() |
![]() |
![]() |
#8 |
Senior Member
Iscritto dal: Jul 2002
Messaggi: 4334
|
Devo usare java5, e comunque non risparmierei una riga di codice...
__________________
|Java Base| |
![]() |
![]() |
![]() |
#9 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
non potendo essere final, lastUpdater e stopper devono essere volatile o incapsulati in un AtomicReference/AtomicBoolean o il loro accesso deve essere contenuto in un blocco sincronizzato.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#10 | |
Senior Member
Iscritto dal: Dec 2000
Città: bologna
Messaggi: 1309
|
Quote:
in quella maniera hai solo spostato piu avanti nel codice il controllo, ma non l'hai eliminato o reso sincronizzato rispetto a piu thread. ps che roba sono le atomicQualcosa? |
|
![]() |
![]() |
![]() |
#11 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Sono i nuovi volatile. Se prima si diceva:
private volatile boolean pimpumpam... oggi si dovrebbe prediligere: private final AtomicBoolean pimpumpam... C'è un Atomic per tipo, con AtomicReference asso piglia tutto.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#12 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Credo che il problema d'ordine sia stato risolto. Quel booleano, uno per ogni Thread, viene controllato in un unico Thread, l'AWT Event Dispatcher. Poichè il Runnable è premuto nella coda degli eventi AWT, esiste una relazione d'ordine tra gli eventi.
Se il Thread è bloccato nell'esecuzione del runnable allora non può verificarsi l'aggiornamento grafico del Thread successivo perchè il Thread successivo ha premuto il suo evento nella stessa coda. Se l'evento del Thread precedente è premuto nella coda prima di quello del Thread successivo, allora quando sarà eseguito il runnable del Thread precedente il valore di stopped sarà necessariamente (volatile o Atomic) true. Insomma, mi pare che funzioni. A naso e alla 22:00, ma direi che funziona. Lì. Quello che mi lascia perplesso è "lastUpdater". E' possibile che un Thread a cui è affidata l'esecuzione di un aggiornamento precedente arrivi a bloccare un Thread a cui è affidata l'esecuzione dell'aggiornamneto successivo. io eseguo: thread 1 (aggiornamento ui vecchio) -> via thread 2 (aggiornamento ui nuovo) -> via thread 1 sta per eseguire: lastUpdater.stop thread 2 tira la volata e arriva fino a lastUpdater = this thread 1 esegue lastUpdate.stop e impedisce l'aggiornamento UI di Thread 2 L'ultimo aggiornamento grafico non viene eseguito. O mi sono perso qualcosa?
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#13 | ||
Senior Member
Iscritto dal: Dec 2000
Città: bologna
Messaggi: 1309
|
Quote:
Quote:
...tanto per far capire quanto sia complicato giocare con i thread |
||
![]() |
![]() |
![]() |
#14 |
Senior Member
Iscritto dal: Jul 2002
Messaggi: 4334
|
Ma ragazzi, il costruttore, in cui chiamo lastUpdater.stop(), viene eseguito nell'EDT,
che e' un unico thread, quindi sequenzialmente, e non parallelamente, o sbaglio? lastUpdater non e' un thread, e' un Runnable eseguito dal thread.
__________________
|Java Base| |
![]() |
![]() |
![]() |
#15 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Se lastUpdater ha un metodo stop è un po' più di un Runnable. Che non sia un Thread l'ho capito
![]() Se il costruttore è eseguito dall'AWT Event Dispatcher allora, contrariamente al classico libro giallo, la trama si semplifica. Il caso che mi lasciava perplesso non si verifica più. lastUpdater non deve essere volatile/atomic: è usato da un solo Thread dunque che sia aggiornata la sola copia locale di quel campo è indifferente. Deve ancora essere volatile/atomic "stopped", perchè il suo valore è letto da un Thread e scritto da un altro.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#16 |
Senior Member
Iscritto dal: Jul 2002
Messaggi: 4334
|
Ma l'assegnamento a un boolean e' comunque atomico... o no?
__________________
|Java Base| |
![]() |
![]() |
![]() |
#17 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
E' un inghippo terminologico. Sì, l'assegnamento è atomico, nello stesso senso in cui non è atomico l'assegnamento ad un long.
Ma se portiamo la faccenda in un contesto concorrente, questa atomicità non è più sufficiente. I thread operano su copie locali dei campi. Tutte le operazioni che un Thread esegue su un campo sono operazioni il cui effetto è limitato alla copia locale di quel campo. Così se in un Thread assegno al campo ciccio il valore N ciò che accade, secondo le specifiche del linguaggio, è che la copia locale del campo ciccio, che esiste solo per quel tal Thread, assume il valore N. Il campo "vero", quello che esiste nell'heap, resta inalterato. Lo stesso vale per la lettura. Quando un Thread legge il valore di un campo legge il valore della copia locale di quel campo. I valori iniziali dei campi usati dai Thread sono quelli esistenti nell'heap. Poichè ogni volta che un Thread muta il valore di un campo tale mutazione è propria della sola copia locale di quel campo, in linea di principio i campi hanno sempre e solo il valore iniziale predefinito (null per i reference, 0 per i numerici, '\0' per i caratteri, false per i boolean). Ci sono poi una serie di norme che stabiliscono quando la manipolazione che un Thread opera su un campo risulta visibile ad altri Thread. Una di queste coinvolge l'uso del modificatore volatile (e del suo alter-ego in Java 5 rappresentato dagli AtomicBlaBla). Detto terra-terra, quando assegni un valore ad un campo volatile quel valore finisce nell'heap e sarà quindi il valore ottenuto da altri Thread quando accederanno a quel campo. Nel nostro caso noi ci chiediamo: qual'è il valore del campo "stopped". Basandoci sulle norme del modello di memoria del linguaggio di programmazione Java noi sappiamo che. 1. stopped ha sicuramente il valore di inizializzazione predefinito "false". 2. nel costruttore quel valore cambia ma quello che veramente cambia è il valore di una copia locale (ad un Thread) di stopped. 3. nel metodo run, il valore certo di stopped è false se il run sia eseguito da un Thread diverso da quello che ha invocato il costruttore. 4. nel metodo run del Runnable usato in invokeLater, il valore certo di stopped è ancora false se il costruttore non sia stato eseguito dall'AWT Event Dispatcher. E' true se, come nel nostro caso, il costruttore sia stato eseguito dall'AWT Event Dispatcher. Ecco perchè dico che "stopped" deve essere volatile: perchè il metodo run è certamente eseguito da un Thread diverso dall'AWT Event Dispatcher. Non serve che sia volatile "lastUpdated". In questo caso, infatti, hai garantico che il costruttore, in cui il valore di lastUpdated cambia, sarà eseguito sempre e soltanto da un solo Thread. Questo Thread lavorerà sulla sua copia locale e questa copia locale assumerà di volta in volta valori diversi.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#18 |
Senior Member
Iscritto dal: Jul 2002
Messaggi: 4334
|
Ah, ok...
Grazie.
__________________
|Java Base| |
![]() |
![]() |
![]() |
#19 |
Senior Member
Iscritto dal: Jul 2002
Messaggi: 4334
|
Una curiosita': questa storia delle variabili locali ai thread vale anche per le statiche?
__________________
|Java Base| |
![]() |
![]() |
![]() |
#20 |
Senior Member
Iscritto dal: Dec 2000
Città: bologna
Messaggi: 1309
|
a naso direi di si, ma aspetterei il guru per la conferma
![]() |
![]() |
![]() |
![]() |
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 04:28.