Sei sulla pagina 1di 6

Thread Java - Introduzione

Primo esempio: il metodo Thread.sleep

Il linguaggio di programmazione Java permette di creare e gestire i thread. A tal proposito, Java fornisce la classe Thread, del package java.lang, che è una sottoclasse diretta della superclasse cosmica Object.

Object T hread
Object
T hread

Prima di iniziare l'analisi della classe Thread, consideriamo il seguente esempio.

Si vuole scrivere un programma che visualizza le stringhe memorizzate in un array, una per volta e a distanza di qualche secondo l'una dall'altra.

Il programma è molto semplice: dopo aver inizializzato l'array è sufficiente inserire in un ciclo for la stampa a video degli elementi dell'array. Per introdurre un ritardo tra una visualizzazione e l'altra, si può utilizzare il metodo sleep della classe Thread.

public static void sleep(long millis ) throws InterruptedException

Sleep è un metodo statico, che interrompe il thread per il numero di millisecondi specificato come parametro. Se il thread è già interrotto, il metodo lancia l'eccezione InterruptedException; pertanto, la chiamata a sleep deve essere racchiusa nel costrutto try.

Il codice del programma è il seguente:

public class SequenzaStringhe

{

public static void main(String[] args)

{

String[] stringhe={"stringa 1","stringa 2","stringa 3"}; int indiceCorrente=-1;

for(int i=1;i<=9;i++)

{

indiceCorrente++;

if(indiceCorrente==stringhe.length)

{

indiceCorrente=0;

}

if (indiceCorrente==stringhe. length ) { indiceCorrente=0; } System. out .println(stringhe[indiceCorrente]); Autore:

System. out .println(stringhe[indiceCorrente]);

Autore: Cinzia Bocchi Ultimo aggiornamento: 18/11/2011

1

try

{

Thread. sleep (4000);

}

catch(InterruptedException e)

{

 
System. out .println( "thread interrotto" );

System. out .println("thread interrotto");

}

}

}

}

Questo semplice programma origina un solo thread, che viene sospeso per 4 secondi. Il thread non può essere interrotto ma solo distrutto, terminando il programma oppure occorre attendere che il ciclo for termini.

Thread di sistema

In Java, esistono un certo numero di thread che vengono avviati dalla virtual machine in modo del tutto trasparente all’utente. Tra questi:

un thread per la gestione delle interfacce grafiche responsabile della cattura

di eventi e dell’aggiornamento dei contenuti dell’interfaccia grafica (thread di comunicazione degli eventi);

garbage collection, responsabile di trovare gli oggetti non più referenziati

il

e

quindi eliminabili dallo spazio di memoria dell’ applicazione;

lo stesso metodo main() di una applicazione che viene avviato come un thread (thread principale) sotto il controllo della java virtual machine.

Secondo esempio: thread e swing

Creiamo una versione con interfaccia grafica del programma del precedente esempio. La finestra principale contiene:

un pannello centrale per visualizzare le stringhe;

un pulsante avvia nell'area sud del frame.

Facendo clic sul pulsante avvia, inizia la visualizzazione delle stringhe.

import java.awt.BorderLayout; import java.awt.Container; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel;

@SuppressWarnings("serial") public class SequenzaStringhe extends JFrame

{

public static final int LARGHEZZA = 300; public static final int ALTEZZA = 300;

private int indiceCorrente=-1; private String stringaCorrente="";

Autore: Cinzia Bocchi Ultimo aggiornamento: 18/11/2011

2

public SequenzaStringhe()

{

super ("Sequenza di stringhe"); setSize( LARGHEZZA , ALTEZZA ); setDefaultCloseOperation(JFrame. EXIT_ON_CLOSE ); Container contentPane= getContentPane();

JButton avvia= new JButton("avvia"); final Canvas canvas= new Canvas(); contentPane.add(avvia,BorderLayout. SOUTH ); contentPane.add(canvas,BorderLayout. CENTER );

final String[] stringhe= {"stringa 1","stringa 2","stringa 3"};

avvia.addActionListener(new ActionListener()

{

public void actionPerformed(ActionEvent e)

{

try

{

for(int i=0;i<9;i++)

 

{

 

indiceCorrente++; System. out .println("indice corrente=

"+indiceCorrente);

 

if(indiceCorrente==stringhe.length)

{

indiceCorrente=0;

}

stringaCorrente=

stringhe[indiceCorrente];

 

canvas.paint(canvas.getGraphics()); Thread. sleep (4000);

 

}

}

catch(InterruptedException ecc)

{

System. err .println("thread interrotto");

}

});

}

}

class Canvas extends JPanel

{

public void paintComponent(Graphics g)

{

super.paintComponent(g); System. out .println("stringa corrente= "+stringaCorrente);

g.drawString(stringaCorrente,100,100);

}

}

public static void main(String args[])

{

SequenzaStringhe frame= new SequenzaStringhe(); frame.setVisible(true);

}

}

Autore: Cinzia Bocchi Ultimo aggiornamento: 18/11/2011

3

Il programma presenta un problema: quando si fa clic sul pulsante avvia, esso rimane disabilitato fino a quando non termina il ciclo for. Inoltre, l'utente non può terminare l'applicazione semplicemente chiudendo la finestra, ma deve aspettare che il for termini; l'unico modo per interrompere l'applicazione è terminarla brutalmente con una operazione di "kill".

Una possibile soluzione: creare un nuovo thread

Il problema verificatosi nel programma dell'esempio precedente è causato dal for contenuto nel gestore dell’evento “clic su avvia”. Infatti, il ciclo for occupa il thread di comunicazione degli eventi, impedendo all'interfaccia di rispondere alle richieste dell'utente. La soluzione consiste nel creare un nuovo thread che svolga l'operazione, indipendentemente dagli altri thread.

In generale, si crea un nuovo thread, quando:

un'azione richiede molto tempo per essere completata (caso precedente); un'azione può bloccarsi su un'operazione di I/O.

richiede molto tempo per essere completata (caso precedente); un'azione può bloccarsi su un'operazione di I/O.

Un modo per creare un nuovo thread è quello di definire una sottoclasse della classe Thread. Il codice seguente utilizza un nuovo thread per visualizzare le stringhe in sequenza. Le differenze rispetto al codice precedente sono evidenziate in grassetto.

mport java.awt.BorderLayout; import java.awt.Container; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel;

@SuppressWarnings("serial")

public class SequenzaStringhe extends JFrame

{

public static final int LARGHEZZA = 300; public static final int ALTEZZA = 300;

private int indiceCorrente=-1; private String stringaCorrente=""; private String[] stringhe= {"stringa 1","stringa 2","stringa 3"}; private Canvas canvas;

public SequenzaStringhe()

{

super ("Sequenza di stringhe"); setSize( LARGHEZZA , ALTEZZA ); setDefaultCloseOperation(JFrame. EXIT_ON_CLOSE ); Container contentPane= getContentPane();

final JButton avvia= new JButton("avvia"); canvas= new Canvas(); contentPane.add(avvia,BorderLayout. SOUTH ); contentPane.add(canvas,BorderLayout. CENTER );

avvia.addActionListener(new ActionListener()

{

Autore: Cinzia Bocchi Ultimo aggiornamento: 18/11/2011

4

public void actionPerformed(ActionEvent e)

{

StringThread athread= new StringThread(); athread.start(); avvia.setEnabled(false);

}

});

}

class Canvas extends JPanel

{

public void paintComponent(Graphics g)

{

super.paintComponent(g); System. out .println("stringa corrente= "+stringaCorrente);

g.drawString(stringaCorrente,100,100);

}

}

class StringThread extends Thread

{

 

public void run()

 

{

try

{

 

while(true)

{

indiceCorrente++; System. out .println("indice corrente= "+indiceCorrente); if(indiceCorrente==stringhe.length)

 

indiceCorrente=0;

 

stringaCorrente= stringhe[indiceCorrente]; canvas.paint(canvas.getGraphics()); Thread. sleep (4000);

}

 

}

catch(InterruptedException ecc)

{

 

System. err .println("thread interrotto");

 

}

}

}

public static void main(String args[])

{

SequenzaStringhe frame= new SequenzaStringhe(); frame.setVisible(true);

}

}

Autore: Cinzia Bocchi Ultimo aggiornamento: 18/11/2011

5

Il codice del gestore dell’evento “clic su avvia” non contiene più il ciclo che consente di visualizzare le immagini in sequenza, ma si limita a compiere le seguenti azioni:

1) Crea un'istanza della classe StringThread:

StringThread athread= new StringThread();

Questo equivale a creare un nuovo thread di nome athread.

2) Avvia il thread:

athread.start();

Il metodo start è un metodo della classe Thread che causa l'inizio dell'esecuzione del thread ed è così definito:

public void start()

3) Disabilita il pulsante avvia:

avvia.setEnabled(false);

La disabilitazione del pulsante avvia non è un'operazione necessaria, ma è stata introdotta per evitare di creare più di un thread aggiuntivo. Infatti, ogni volta che si fa clic sul pulsante avvia, viene creato un nuovo thread.

In generale, il gestore di un evento che richiede la creazione di un nuovo thread deve:

creare il thread; avviare il thread.

La classe StringThread è derivata dalla classe Thread e contiene il codice da eseguire in un nuovo thread. Al suo interno è ridefinito ( overriden ) il metodo run della classe Thread (tutte le sottoclassi di Thread devono ridefinire tale metodo), la cui firma è:

public void run()

Il metodo run non deve essere invocato esplicitamente dal programmatore poiché la sua chiamata avviene mediante il metodo start. Il metodo run del nostro esempio contiene le istruzioni che in precedenza si trovavano nel metodo actionPerformed, con l'unica differenza che il ciclo for è stato sostituito da un ciclo while, potenzialmente infinito. Questa sostituzione non è necessaria per la gestione dei thread, ma è una consuetudine.