Comunicazione tra thread in Java

Benvenuti, aspiranti programmatori Java! Oggi intraprenderemo un viaggio avventuroso nel mondo della comunicazione tra thread in Java. Come vostra insegnante di informatica di fiducia, sono qui per guidarvi attraverso questo affascinante argomento. Quindi, prenda il suo bevanda preferita, si rilassi e... immergiamoci!

Java - Inter-thread Communication

Cos'è la Comunicazione tra thread?

Immagina di essere in una gara di staffetta. Devi passare il bastone al tuo compagno al momento giusto. Questo è sostanzialmente ciò che significa la comunicazione tra thread nel mondo della programmazione. È il modo in cui i diversi thread di un programma Java comunicano tra loro, coordinano le loro azioni e condividono informazioni.

Perché la Comunicazione tra thread è Importante?

La comunicazione tra thread è cruciale per creare programmi efficienti e sincronizzati. Senza di essa, i nostri thread sarebbero come corridori in corse separate, incapaci di collaborare o condividere risorse in modo efficace.

Metodi Utilizzati per la Comunicazione tra thread

Java fornisce diversi metodi per la comunicazione tra thread. Guardiamo una tabella utile:

Metodo Descrizione
wait() Causa il thread corrente di attendere fino a quando un altro thread invoca i metodi notify() o notifyAll() per questo oggetto
notify() Sveglia un solo thread che sta aspettando sul monitor di questo oggetto
notifyAll() Sveglia tutti i thread che stanno aspettando sul monitor di questo oggetto

Ora, spezziamo questi down e vediamo come funzionano in azione!

Il Metodo wait()

Il metodo wait() è come dire a un thread: "Ehi, fai una pausa fino a quando qualcuno ti dà una spintarella." Ecco come funziona:

synchronized(object) {
while(condition) {
object.wait();
}
}

In questo codice:

  1. Prima sincronizziamo un oggetto per garantire la sicurezza dei thread.
  2. Controlliamo una condizione in un ciclo while.
  3. Se la condizione è vera, chiamiamo wait(), il che fa sì che il thread si metta in pausa e attenda una notifica.

Il Metodo notify()

Il metodo notify() è come toccare un thread in attesa sulla spalla e dire: "Sveglia! È il tuo turno ora." Ecco come lo usiamo:

synchronized(object) {
// Cambia la condizione
condition = true;
object.notify();
}

In questo codice:

  1. Sincronizziamo sullo stesso oggetto come nella chiamata wait().
  2. Cambiamo la condizione che il thread in attesa stava controllando.
  3. Chiamiamo notify() per svegliare un thread in attesa.

Il Metodo notifyAll()

Il metodo notifyAll() è come gridare: "Tutti svegliate!" È usato quando si vuole avvisare tutti i thread in attesa. Ecco un esempio:

synchronized(object) {
// Cambia la condizione
condition = true;
object.notifyAll();
}

Questo funziona simile a notify(), ma sveglia tutti i thread in attesa invece che solo uno.

Un Esempio Reale: Il Problema Produttore-Consumatore

Mettiamo tutto questo insieme con un esempio classico: il Problema Produttore-Consumatore. Immagina una pasticceria in cui una persona (il produttore) fa il pane, e un'altra (il consumatore) lo vende. Condividono uno spazio di scaffale limitato.

Ecco come possiamo implementare questo in Java:

class Pasticceria {
private int pane = 0;
private final int CAPACITA = 5;

public synchronized void producePane() throws InterruptedException {
while (pane == CAPACITA) {
System.out.println("Lo scaffale è pieno! Il pasticciere attende...");
wait();
}
pane++;
System.out.println("Il pasticciere ha fatto un pane. Totale: " + pane);
notify();
}

public synchronized void sellPane() throws InterruptedException {
while (pane == 0) {
System.out.println("Niente pane! Il venditore attende...");
wait();
}
pane--;
System.out.println("Venduto un pane. Rimanenti: " + pane);
notify();
}
}

class Pasticciere implements Runnable {
private Pasticceria pasticceria;

public Pasticciere(Pasticceria pasticceria) {
this.pasticceria = pasticceria;
}

public void run() {
for (int i = 0; i < 10; i++) {
try {
pasticceria.producePane();
Thread.sleep(1000); // Tempo per cuocere
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

class Venditore implements Runnable {
private Pasticceria pasticceria;

public Venditore(Pasticceria pasticceria) {
this.pasticceria = pasticceria;
}

public void run() {
for (int i = 0; i < 10; i++) {
try {
pasticceria.sellPane();
Thread.sleep(1500); // Tempo tra le vendite
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

public class PasticceriaDemo {
public static void main(String[] args) {
Pasticceria pasticceria = new Pasticceria();
Thread pasticciere = new Thread(new Pasticciere(pasticceria));
Thread venditore = new Thread(new Venditore(pasticceria));

pasticciere.start();
venditore.start();
}
}

Spiegazione:

  1. Abbiamo una classe Pasticceria che gestisce l'inventario del pane.
  2. Il metodo producePane() rappresenta il pasticciere che fa il pane. Se lo scaffale è pieno, il pasticciere attende.
  3. Il metodo sellPane() rappresenta il venditore che vende il pane. Se non c'è pane, il venditore attende.
  4. Usiamo wait() quando le condizioni non sono ottimali per produrre o vendere.
  5. Usiamo notify() dopo aver prodotto o venduto per avvisare l'altro thread.
  6. Le classi Pasticciere e Venditore girano in thread separati, cercando continuamente di produrre o vendere pane.

Quando esegui questo programma, vedrai il pasticciere e il venditore lavorare insieme, attendendo quando necessario e avvisandosi l'un l'altro quando possono procedere. È come guardare una danza ben coordinata!

Conclusione

Ed eccoci qua, ragazzi! Abbiamo viaggiato attraverso la terra della comunicazione tra thread in Java. Abbiamo visto come i thread possono coordinare le loro azioni usando wait(), notify() e notifyAll(). Abbiamo persino costruito una pasticceria virtuale per vedere questi concetti in azione!

Ricorda, come nel nostro esempio della pasticceria, una buona comunicazione tra thread è tutto una questione di equilibrio e coordinamento. È sapere quando lavorare, quando attendere e quando segnalare agli altri. Padroneggia questo, e sarai ben accolto per creare programmi Java efficienti e ben coordinati.

Continua a praticare, mantieniti curioso e buon coding! E ricorda, sia nella programmazione che nella vita, una buona comunicazione è la chiave del successo. Alla prossima volta, questo è il tuo insegnante di informatica di fiducia, che firma off!

Credits: Image by storyset