I thread in Java vengono creati estendendo la classe Thread
e definendo il metodo run
(il codice che il thread eseguirà).
Per eseguire il nuovo thread è sufficiente invocare il metodo start
.
public class CreaThread extends Thread {
public void run() {
System.out.println("Saluti dal thread " + this.getName());
}
public static void main(String args[]) {
CreaThread t = new CreaThread();
t.start();
}
}
Se c’è esigenza di estendere altre classi, i thread possono anche essere creati come oggetti della classe Thread
, al cui costruttore viene passato un oggetto che implementa l’interfaccia Runnable
, che richiede l’implementazione del metodo run
.
public class CreaThread2 implements Runnable {
public void run() {
System.out.println("Saluti dal thread "
+ Thread.currentThread().getName());
}
public static void main(String args[]) {
CreaThread2 r = new CreaThread2();
Thread t = new Thread(r);
t.start();
}
}
Come per i thead POSIX, possiamo anche “addormentare” un thread Java, in questo caso il tempo è dato in millisecondi ed è necessario gestire l’eccezione InterruptedException
sollevata da sleep nel caso il thread t
venga interrotto ad esempio tramite t.interrupt()
.
public void run() {
try {
sleep(1000); // attende un secondo
} catch(InterruptedException e) {
System.out.println("["+getName()+"]"+" mi hanno interrotto!");
return;
}
Creare n thread, rallentarli tramite sleep()
, provare ad interromperne l’attesa utilizzando t.interrupt()
e attendere la terminazione con t.join()
(analogamente a quanto si fa con i thread POSIX), dove t
è l’oggetto thread.
Aggiungere opportune print per osservare l’esecuzione.
(La soluzione è sulle note, guardatela dopo aver provato da soli!)
In Java è implementata una forma semplificata dei monitor con le seguenti caratteristiche:
synchronized
;
wait(), notify(), notifyAll()
;
synchronized(this) {
contatore=contatore+1
}
`this’ indica a quale oggetto fare riferimento per ottenere il lock implicito. In questo caso è l’oggetto stesso ma è possibile indicare oggetti differenti e quindi sincronizzarsi utilizzando il mutex di tali oggetti.
NOTA: la sincronizzazione è “rientrante”: è possibile chiamare un metodo synchronized
da un altro synchronized
senza problemi.
Implementare due thread che lavorano su un contatore condiviso, osservare le usuali interferenze e rimediare ponendo il codice all’interno di metodi synchronized
o all’interno del costrutto synchronized(this) { ... }
.
Suggerimento: mettere il contatore in una classe apposita (che fungerà da monitor) e implementare i metodi per l’incremento e la lettura/stampa del valore.
La soluzione è nelle note, guardatela dopo aver provato da soli.
C’è un terzo esercizio sulle note (produttore e consumatore)!