Java 101: comprensione dei thread Java, parte 4: gruppi di thread, volatilità e variabili locali del thread

Java 101 di questo mese conclude la serie di thread concentrandosi su gruppi di thread, volatilità, variabili locali di thread, timer e ThreadDeathclasse.

Capire i thread Java: leggi l'intera serie

  • Parte 1: presentazione di thread ed eseguibili
  • Parte 2: sincronizzazione dei thread
  • Parte 3: pianificazione dei thread, attesa / notifica e interruzione del thread
  • Parte 4: gruppi di thread, volatilità, variabili locali del thread, timer e morte del thread

Gruppi di thread

In un programma server di rete, un thread attende e accetta richieste dai programmi client per eseguire, ad esempio, transazioni di database o calcoli complessi. Il thread di solito crea un nuovo thread per gestire la richiesta. A seconda del volume della richiesta, potrebbero essere presenti molti thread diversi contemporaneamente, complicando la gestione dei thread. Per la gestione dei thread semplificare, programmi organizzano loro fili con gruppi di thread - java.lang.ThreadGroupoggetti che discussioni relativo gruppo Thread(e Threadsottoclasse) oggetti. Ad esempio, il programma può utilizzare ThreadGroupper raggruppare tutti i thread di stampa in un gruppo.

Nota: per mantenere la discussione semplice, mi riferisco ai gruppi di thread come se organizzassero i thread. In realtà, i gruppi di thread organizzano Thread(e Threadsottoclasse) gli oggetti associati ai thread.

Java richiede che ogni thread e ogni gruppo di thread, salvo il gruppo di thread radice, si systemuniscano a un altro gruppo di thread. Questa disposizione porta a una struttura gerarchica di gruppi di thread, che la figura seguente illustra in un contesto dell'applicazione.

Nella parte superiore della struttura della figura c'è il systemgruppo di fili. Il systemgruppo creato da JVM organizza i thread JVM che si occupano della finalizzazione degli oggetti e di altre attività di sistema e funge da gruppo di thread radice della struttura gerarchica del gruppo di thread di un'applicazione. Appena sotto systemc'è il maingruppo di thread creato da JVM , che è systemil sottogruppo di thread (sottogruppo, in breve). maincontiene almeno un thread: il thread principale creato da JVM che esegue le istruzioni byte-code nel main()metodo.

Sotto il maingruppo risiedono i sottogruppi subgroup 1e subgroup 2, i sottogruppi creati dall'applicazione (che l'applicazione della figura crea). Inoltre, subgroup 1gruppi di tre fili applicativi creati: thread 1, thread 2e thread 3. Al contrario, subgroup 2i gruppi un thread di applicazione-creato: my thread.

Ora che conosci le basi, iniziamo a creare gruppi di thread.

Crea gruppi di thread e associa i thread a quei gruppi

La ThreadGroupdocumentazione SDK della classe rivela due costruttori: ThreadGroup(String name)e ThreadGroup(ThreadGroup parent, String name). Entrambi i costruttori creano un gruppo di thread e gli assegnano un nome, come namespecificato dal parametro. I costruttori differiscono nella scelta del gruppo di thread che funge da genitore per il gruppo di thread appena creato. Ogni gruppo di thread, ad eccezione system, deve avere un gruppo di thread padre. Infatti ThreadGroup(String name), il genitore è il gruppo di thread del thread che chiama ThreadGroup(String name). Ad esempio, se il filo principale chiama ThreadGroup(String name), il gruppo di thread appena creato ha gruppo del thread principale come genitore main. Perché ThreadGroup(ThreadGroup parent, String name)il genitore è il gruppo a cui fa parentriferimento. Il codice seguente mostra come utilizzare questi costruttori per creare una coppia di gruppi di thread:

public static void main (String [] args) { ThreadGroup tg1 = new ThreadGroup ("A"); ThreadGroup tg2 = new ThreadGroup (tg1, "B"); }

Nel codice precedente, il thread principale crea due gruppi di thread: Ae B. Innanzitutto, il thread principale crea Achiamando ThreadGroup(String name). Il tg1genitore del gruppo di thread di riferimento è mainperché mainè il gruppo di thread del thread principale. In secondo luogo, il thread principale crea Bchiamando ThreadGroup(ThreadGroup parent, String name). Il tg2genitore del gruppo di thread a cui si fa riferimento è Aperché tg1il riferimento di passa come argomento ThreadGroup (tg1, "B")e si Aassocia tg1.

Suggerimento: una volta che non è più necessaria una gerarchia di ThreadGroupoggetti, chiamare ThreadGroupil void destroy()metodo tramite un riferimento ThreadGroupall'oggetto all'inizio di quella gerarchia. Se l' ThreadGroupoggetto principale e tutti gli oggetti del sottogruppo mancano di oggetti thread, destroy()prepara tali oggetti gruppo thread per la garbage collection. Altrimenti, destroy()lancia un IllegalThreadStateExceptionoggetto. Tuttavia, fino a quando non si annulla il riferimento ThreadGroupall'oggetto superiore (supponendo che una variabile di campo contenga quel riferimento), il garbage collector non può raccogliere quell'oggetto. Facendo riferimento all'oggetto in alto, è possibile determinare se è stata effettuata una chiamata precedente al destroy()metodo chiamando ThreadGroupil boolean isDestroyed()metodo di. Questo metodo restituisce true se la gerarchia del gruppo di thread è stata eliminata.

Di per sé, i gruppi di thread sono inutili. Per essere di qualsiasi utilità, devono raggruppare i thread. Raggruppa i thread in gruppi di thread passando i ThreadGroupriferimenti ai Threadcostruttori appropriati :

ThreadGroup tg = new ThreadGroup ("subgroup 2"); Thread t = new Thread (tg, "my thread");

Il codice sopra prima crea un subgroup 2gruppo con maincome gruppo padre. (Presumo che il thread principale esegua il codice.) Il codice successivamente crea un my threadThreadoggetto nel subgroup 2gruppo.

Ora, creiamo un'applicazione che produca la struttura gerarchica del gruppo di thread della nostra figura:

Listato 1. ThreadGroupDemo.java

// ThreadGroupDemo.java class ThreadGroupDemo { public static void main (String [] args) { ThreadGroup tg = new ThreadGroup ("subgroup 1"); Thread t1 = new Thread (tg, "thread 1"); Thread t2 = new Thread (tg, "thread 2"); Thread t3 = new Thread (tg, "thread 3"); tg = new ThreadGroup ("subgroup 2"); Thread t4 = new Thread (tg, "my thread"); tg = Thread.currentThread ().getThreadGroup (); int agc = tg.activeGroupCount (); System.out.println ("Active thread groups in " + tg.getName () + " thread group: " + agc); tg.list (); } }

ThreadGroupDemocrea il gruppo di thread e gli oggetti thread appropriati per rispecchiare ciò che vedi nella figura sopra. Per dimostrare che i gruppi subgroup 1e subgroup 2sono maingli unici sottogruppi, ThreadGroupDemoprocedi come segue:

  1. Recupera un riferimento al thread principale ThreadGroupoggetto chiamando Thread's statico currentThread()metodo (che restituisce un riferimento del thread principale Threadoggetto) seguiti da Thread' s ThreadGroup getThreadGroup()metodo.
  2. Chiama ThreadGroupil int activeGroupCount()metodo sul ThreadGroupriferimento appena restituito per restituire una stima dei gruppi attivi all'interno del gruppo di thread del thread principale.
  3. Chiama ThreadGroupil String getName ()metodo di restituire il nome del gruppo di thread del thread principale.
  4. Chiama ThreadGroupil void list ()metodo di stampare sui dettagli del dispositivo di output standard sul gruppo di thread del thread principale e su tutti i sottogruppi.

Quando viene eseguito, ThreadGroupDemovisualizza il seguente output:

Active thread groups in main thread group: 2 java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main] Thread[Thread-0,5,main] java.lang.ThreadGroup[name=subgroup 1,maxpri=10] Thread[thread 1,5,subgroup 1] Thread[thread 2,5,subgroup 1] Thread[thread 3,5,subgroup 1] java.lang.ThreadGroup[name=subgroup 2,maxpri=10] Thread[my thread,5,subgroup 2]

Uscita che inizia con Threadi risultati di list()'s chiamate interne a Thread' s toString()metodo, un formato di output che ho descritto nella Parte 1. Insieme a questo l'uscita, si vede l'uscita a cominciare java.lang.ThreadGroup. Quell'output identifica il nome del gruppo di thread seguito dalla sua massima priorità.

Priorità e gruppi di thread

La massima priorità di un gruppo di thread è la massima priorità che qualsiasi thread può raggiungere. Considera il suddetto programma per server di rete. All'interno di quel programma, un thread attende e accetta le richieste dai programmi client. Prima di farlo, il thread wait-for / accept-request potrebbe prima creare un gruppo di thread con una priorità massima appena al di sotto della priorità di quel thread. Successivamente, quando arriva una richiesta, il thread wait-for / accept-request crea un nuovo thread per rispondere alla richiesta del client e aggiunge il nuovo thread al gruppo thread creato in precedenza. La priorità del nuovo thread si abbassa automaticamente al massimo del gruppo di thread. In questo modo, il thread wait-for / accept-request risponde più spesso alle richieste perché viene eseguito più spesso.

Java assegna una priorità massima a ciascun gruppo di thread. Quando crei un gruppo, Java ottiene quella priorità dal suo gruppo padre. Utilizzare ThreadGroupil void setMaxPriority(int priority)metodo di per impostare successivamente la priorità massima. Tutti i thread aggiunti al gruppo dopo aver impostato la priorità massima non possono avere una priorità che supera il massimo. Qualsiasi thread con una priorità più alta si abbassa automaticamente quando si unisce al gruppo di thread. Tuttavia, se si utilizza setMaxPriority(int priority)per abbassare la priorità massima di un gruppo, tutti i thread aggiunti al gruppo prima della chiamata al metodo mantengono le loro priorità originali. Ad esempio, se aggiungi un thread con priorità 8 a un gruppo con priorità massima 9 e quindi abbassi la priorità massima di quel gruppo a 7, il thread con priorità 8 rimane a priorità 8. In qualsiasi momento, puoi determinare un gruppo di thread 'priorità massima di s chiamando ThreadGroup'sint getMaxPriority()metodo. Per dimostrare priorità e gruppi di thread, ho scritto MaxPriorityDemo:

Listato 2. MaxPriorityDemo.java

// MaxPriorityDemo.java class MaxPriorityDemo { public static void main (String [] args) { ThreadGroup tg = new ThreadGroup ("A"); System.out.println ("tg maximum priority = " + tg.getMaxPriority ()); Thread t1 = new Thread (tg, "X"); System.out.println ("t1 priority = " + t1.getPriority ()); t1.setPriority (Thread.NORM_PRIORITY + 1); System.out.println ("t1 priority after setPriority() = " + t1.getPriority ()); tg.setMaxPriority (Thread.NORM_PRIORITY - 1); System.out.println ("tg maximum priority after setMaxPriority() = " + tg.getMaxPriority ()); System.out.println ("t1 priority after setMaxPriority() = " + t1.getPriority ()); Thread t2 = new Thread (tg, "Y"); System.out.println ("t2 priority = " + t2.getPriority ()); t2.setPriority (Thread.NORM_PRIORITY); System.out.println ("t2 priority after setPriority() = " + t2.getPriority ()); } }

Quando viene eseguito, MaxPriorityDemoproduce il seguente output:

tg maximum priority = 10 t1 priority = 5 t1 priority after setPriority() = 6 tg maximum priority after setMaxPriority() = 4 t1 priority after setMaxPriority() = 6 t2 priority = 4 t2 priority after setPriority() = 4

Il gruppo di thread A(che fa tgriferimento) inizia con la priorità più alta (10) come massimo. Il thread X, il cui Threadoggetto fa t1riferimento, si unisce al gruppo e riceve 5 come priorità. Modifichiamo la priorità di quel thread a 6, che riesce perché 6 è minore di 10. Successivamente, chiamiamo setMaxPriority(int priority)per ridurre la priorità massima del gruppo a 4. Sebbene il thread Xrimanga alla priorità 6, un Ythread appena aggiunto riceve 4 come priorità. Infine, un tentativo di aumentare Yla priorità del thread a 5 fallisce, perché 5 è maggiore di 4.

Nota:setMaxPriority(int priority) regola automaticamente la priorità massima dei sottogruppi di un gruppo di thread.

Oltre a utilizzare i gruppi di thread per limitare la priorità del thread, è possibile eseguire altre attività chiamando vari ThreadGroupmetodi che si applicano al thread di ogni gruppo. I metodi includono void suspend(), void resume(), void stop(), e void interrupt(). Poiché Sun Microsystems ha deprecato i primi tre metodi (non sono sicuri), li esaminiamo solo interrupt().

Interrompere un gruppo di thread

ThreadGroupIl interrupt()metodo di consente a un thread di interrompere i thread e i sottogruppi di un gruppo di thread specifico. Questa tecnica risulterebbe appropriata nel seguente scenario: Il thread principale dell'applicazione crea più thread che eseguono ciascuno un'unità di lavoro. Poiché tutti i thread devono completare le rispettive unità di lavoro prima che qualsiasi thread possa esaminare i risultati, ogni thread attende dopo aver completato la propria unità di lavoro. Il thread principale controlla lo stato di lavoro. Una volta che tutti gli altri thread sono in attesa, il thread principale chiama interrupt()per interrompere le attese degli altri thread. Quindi quei thread possono esaminare ed elaborare i risultati. Il listato 3 mostra l'interruzione del gruppo di thread:

Listato 3. InterruptThreadGroup.java

// InterruptThreadGroup.java class InterruptThreadGroup { public static void main (String [] args) { MyThread mt = new MyThread (); mt.setName ("A"); mt.start (); mt = new MyThread (); mt.setName ("B"); mt.start (); try { Thread.sleep (2000); // Wait 2 seconds } catch (InterruptedException e) { } // Interrupt all methods in the same thread group as the main // thread Thread.currentThread ().getThreadGroup ().interrupt (); } } class MyThread extends Thread { public void run () { synchronized ("A") { System.out.println (getName () + " about to wait."); try { "A".wait (); } catch (InterruptedException e) { System.out.println (getName () + " interrupted."); } System.out.println (getName () + " terminating."); } } }