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)!