PDA

View Full Version : [JAVA] Terminazione Thread


13ciccio87
25-01-2014, 19:32
Salve a tutti, ho creato un programma che sfrutta i thread per effettuare una esecuzione ottimizzata di una serie di job.
La struttura del programma è composta da un thread padre che genera altri thread che a loro volta possono generare altri thread.
Dopo aver creato questo bell'albero di thread mi rimane un problema, vorrei sapere quando tutti questi thread terminano e stampare a video che hanno terminato.
Non credo vi sia bisogno di codice, la struttura del programma è molto semplice.
PS: ho provato ad usare anche il pool di thread, ma non riesco a venirne fuori.
Grazie a coloro che mi aiuteranno.

mone.java
25-01-2014, 20:38
Salve a tutti, ho creato un programma che sfrutta i thread per effettuare una esecuzione ottimizzata di una serie di job.
La struttura del programma è composta da un thread padre che genera altri thread che a loro volta possono generare altri thread.
Dopo aver creato questo bell'albero di thread mi rimane un problema, vorrei sapere quando tutti questi thread terminano e stampare a video che hanno terminato.
Non credo vi sia bisogno di codice, la struttura del programma è molto semplice.
PS: ho provato ad usare anche il pool di thread, ma non riesco a venirne fuori.
Grazie a coloro che mi aiuteranno.

Se quello di cui necessiti è aspettare la terminazione di tutti i thread e solo allora stampare a video qualcosa puoi usare la funzione join()... Mettiamo che tu abbia tutti i tuoi Thread dentro a un Array/Lista... Una volta che li hai lanciati tutti fai un for chiamando join su ognuno di essi...

13ciccio87
25-01-2014, 21:07
Ti ringrazio per la risposta, ma purtroppo non ho la possibilità di tenere "sott'occhio" tutti i thread.
In sostanza partono autonomamente, e in maniera totalmente sporadica, perchè ogni thread può lanciarne a sua volta degli altri, quindi non ho la possibilità di sapere quanti sono o quindi memorizzarli in un array o lista.
Per come è ora il mio programma avvio i thread e poi l'applicativo termina quando tutti i thread terminano, ma non vorrei che un thread fosse "appesantito" nel tener conto se i thread che ha generato sono attivi.
Potrei provare a creare una classe con un metodo che ogni qualvolta viene creato un thread quest'ultimo viene inserito in un array, ma anche in quel caso non posso sapere se i thread che ho lanciato fino a quel momento sono tutti o ne manca qualcuno. Mi spiego meglio, se i thread che vengono lanciati sono 10 in totale (fra thread principale e sottothread creati dinamicamente) potrei creare man mano la lista che contiene i thread e chiamare su ognuno di essi la join, ma potrei avere che ho listato 7 thread terminati e facendo la join vedo che sono tutti effettivamente terminati, ma i restanti 3 potrebbero partire dopo.
A me servirebbe un modo per far dire ad ogni thread (magari ad un metodo) "avviato" (quando viene creato e avviato) e "ho terminato" (quando termina).
Forse sono un pò contorto nello spiegarmi, ma non saprei fare di meglio.

Edit: ho provato a listare man mano i thread, ma ovviamente ho un errore per modifica concorrente della lista, infatti la lista viene modificata dinamicamente e il for che và a chiamare join su ognuno di essi la tiene occupata finchè non termina il thread, ma intando altri thread cercano di inserirsi, quindi ConcurrentModificationException

13ciccio87
25-01-2014, 21:44
Mi autorispondo con una soluzione che ho trovato online, ma spero non sia troppo "grezza".
In sostanza, dopo che ho avviato il thread padre scrivo:

while(Thread.activeCount() > 1){}

Uso il > 1 perchè il thread Principale (l'applicativo che genera il thread padre è di per sè già un Thread) deve essere ancora in esecuzione.
Così sembra funzionare, dato che ho lancitato una esecuzione controllata avviando 14 thread (totali).
Secondo voi è troppo grezzo? Credo che proverò a fare dei test per vedere se funziona a casaccio o è sempre vero...

sharkkk
26-01-2014, 02:52
scusate, qual'e la differenza tra un processo e un thread?

wingman87
26-01-2014, 15:24
La differenza fondamentale è che uno è parte dell'altro, un processo può essere composto da uno o più thread. Da questo poi derivano altre differenze. Ti consiglio di leggere il paragrafo sull'argomento su wikipedia: http://it.wikipedia.org/wiki/Processo_%28informatica%29#Processi_e_thread

Daniels118
27-01-2014, 09:02
@13ciccio87
Quel metodo, oltre ad essere "grezzo", sta li a trasformare la tua CPU in una stufetta.
Prima di tutto devi utilizzare un metodo synchronized per evitare update concorrenti:
http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
Secondo, invece di utilizzare una lista, è sufficiente una variabile intera che viene incrementata per ogni thread avviato e decrementata per ogni thread terminato.
Terzo, utilizza il metodo wait per bloccare l'esecuzione del thread principale, e il metodo notify per sbloccarlo quando il contatore arriva a zero.

13ciccio87
27-01-2014, 10:46
Grazie, ma hai risposto al secondo post parlando del primo. Il primo punto lo conosco perfettamente, quando eclipse mi ha avvertito che c'era una modifica concorrente sapevo di dover usare un synchronized. Punto secondo avevo già usato il contatore e inutile dirti che arriva a zero anche quando non sono terminati tutti i thread (altrimenti non stavo qua a usare liste e altre cose), infatti, con il contatore avevo trovato una soluzione ottimale, mi bastava chiedere se ogni volta che decrementavo il contatore era arrivato a zero, purtroppo arriva a zero prima che i thread siano terminati tutti. Per terzo è una buona idea, ma la risposta sta nel punto due.

Per ora, la soluzione "grezza" del wihile sembra funzionare alla grande, l'unico problema è che durante il time slice dedicato al thread che contiene il while consumo il tempo di CPU a fare cicli inutili.
Il contatore sarebbe l'idea migliore, ma non riesco a farlo funzionare.

mone.java
27-01-2014, 11:04
Punto secondo avevo già usato il contatore e inutile dirti che arriva a zero anche quando non sono terminati tutti i thread (altrimenti non stavo qua a usare liste e altre cose), infatti, con il contatore avevo trovato una soluzione ottimale, mi bastava chiedere se ogni volta che decrementavo il contatore era arrivato a zero, purtroppo arriva a zero prima che i thread siano terminati tutti. Per terzo è una buona idea, ma la risposta sta nel punto due.

Per ora, la soluzione "grezza" del wihile sembra funzionare alla grande, l'unico problema è che durante il time slice dedicato al thread che contiene il while consumo il tempo di CPU a fare cicli inutili.
Il contatore sarebbe l'idea migliore, ma non riesco a farlo funzionare.

Infatti è meglio che tu cerchi una soluzione al contatore globale che sicuramente è la cosa migliore... Per consumare meno CPU nel while puoi metter un Thread.sleep(). Il problema della tua soluzione è che non c'è da fidarsi tanto della "conta globale dei Thread) siccome ne possono essere lanciati altri da altre parti dell'applicazione...Tu dove lo metti l'incremento/decremento dei thread?

Daniels118
27-01-2014, 12:11
La soluzione del contatore ti posso assicurare che funziona, l'ho già utilizzata in passato, se a te non va probabilmente non l'hai implementata correttamente.
Il contatore deve essere unico, un buon posto dove dichiararlo sarebbe nella classe dove si trova il thread principale. Nella stessa classe definisci anche i metodi pubblici incrementa e decrementa, in modo da avere una gestione centralizzata. Dichiara il contatore privato e i metodi synchronized pubblici, così sei sicuro che il contatore venga modificato sempre da un unico thread alla volta. Inoltre, se la tua applicazione lo consente, può essere conveniente dichiarare i metodi static (e di conseguenza anche il contatore), in modo da non dover passare ai thread l'istanza sulla quale invocare i metodi.
Ricordati di gestire anche il caso in cui un thread termini inaspettatamente, altrimenti il contatore non arriverà mai a zero.

13ciccio87
27-01-2014, 12:38
Ovviamente non posso che dare ragione a voi, mi riesco sempre a perdere nelle banalità e poi dico fesserie.
Il contatore lo mettevo nel thread, ma incrementarlo nel thread è sbagliato, perché potrei avere casi in cui un thread non viene avviato in tempo e quindi non incremento il contatore. Mettendo l'incremento nel costruttore del thread (e il decremento prima del break che termina il while(true))ora funziona bene.
La soluzione dell'activeCount non mi piaceva perché avrei contato i thread totali e non quelli che servivano a me (quindi non avrei potuto avviare altri thread).
Purtroppo non posso usare i metodi (e variabili) static perché la classe contiene altri metodi che dovrei far divenire static (riflettero sul farlo o meno, voi dite che conviene più lo static per non portarmi appresso il peso del collegamento alla classe?).
Comunque ora va, ed è questo l'importante.

Daniels118
27-01-2014, 13:03
Non c'è bisogno di dichiarare la classe static per avere proprietà e metodi statici, le due cose possono coesistere.

Comunque io l'incremento non lo metterei nel costruttore, perché non è quello il momento in cui il thread parte; lo metterei invece nel metodo run:
public void run() {
try {
Principale.incrementa();
//CODICE
} catch (Throwable e0) {
} finally {
Principale.decrementa();
}
}

13ciccio87
27-01-2014, 13:23
Quando dissi che il contatore non funzionava era proprio perché lo mettevo nel run.

Daniels118
27-01-2014, 13:31
Ah, ho capito cosa intendi, però assicurati di incrementare il contatore dopo la chiamata al metodo start, perché se questo genera un'eccezione ti ritrovi il contatore incrementato senza che il thread venga creato. Nel metodo run metti sempre chatch Throwable e il decremento nel finally, altrimenti in caso di eccezione rischi di non decrementare il contatore.

13ciccio87
24-02-2014, 00:10
Ah, ho capito cosa intendi, però assicurati di incrementare il contatore dopo la chiamata al metodo start, perché se questo genera un'eccezione ti ritrovi il contatore incrementato senza che il thread venga creato. Nel metodo run metti sempre chatch Throwable e il decremento nel finally, altrimenti in caso di eccezione rischi di non decrementare il contatore.

Mi scuso per il ritardo nel tornare, ma ci tenevo a ringraziarti, il tuo suggerimento mi è stato molto utile e l'uso dei thread per parallelizzare il tutto ha dato esiti negativi (inizialmente, per questo sono scomparso), ma poi ho trovato il giusto modo per dividere i carichi.