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.

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.

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:

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().

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.

(La soluzione è in basso, guardatela dopo aver provato da soli)

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:

    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 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.

Esercizio 3: produttore e consumatore

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

Soluzioni

Ecco le soluzioni ai primi due esercizi.

Soluzione esercizio 1

che dà il seguente output:

Soluzione esercizio 2

Eseguirlo con o senza il `synchronized’ nel metodo incrementa per osservare le interferenze. Dimensionare MAX opportunamente.

1 thought on “Thread in Java”

Leave a Reply

Your email address will not be published. Required fields are marked *