Thread in Java

Il linguaggio Java implementa una forma semplificata dei Monitor. Come prima cosa vediamo come creare un thread in Java.

Creazione di Thread in Java

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();
    }
}

Oppure, 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();
    }
}

Notare che in questo caso per stampare il nome del thread si deve usare il metodo currentThread() della classe Thread. Nella prima soluzione, invece, possiamo direttamente invocare getName() sull’oggetto corrente in quanto istanza della classe Thread.

Entrambi i programmi danno il seguente output:

> javac CreaThread.java
> java CreaThread
Saluti dal thread Thread-0
>

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()+"]"+" Ah mi hanno interrotto!!");
            return; 
        } 

Esercizio 1: creazione, interruzione e attesa di thread

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.

I Monitor di Java

In Java è implementata una forma semplificata dei monitor con le seguenti caratteristiche:

  • Ogni oggetto ha un mutex implicito utilizzato per garantire mutua esclusione sui metodi;
  • I metodi sono eseguiti in mutua esclusione solo se dichiarati synchronized;
  • Ogni oggetto ha un’unica condition implicita sulla quale si possono effettuare le operazioni standard wait(), notify(), notifyAll();
  • se il metodo è statico allora il mutex è a livello di classe invece che di oggetto;
  • è inoltre possibile sincronizzare parti di codice di un metodo non synchronized nel seguente modo:
    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 (il mutex è già acquisito alla chiamata del primo metodo sincronizzato e viene mantenuto fino al ritorno del metodo o ad una eventuale terminazione prematura per via di un’eccezione non gestita)

Esercizio 2: sezione critica

Implementare due thread che lavorano su un contatore condiviso, osservare le usuali interferenze e rimediare ponendo il codice all’interno di metodi synchronized.

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.

Esercizio 3: produttore e consumatore

Implementare il problema del produttore e consumatore in Java. Testarlo con tanti produttori e tanti consumatori.

Suggerimento: seguire lo schema illustrato nella lezione sui monitor.