IL

"Ciao, Amico!"

"Voglio approfondire con te per quanto riguarda wait-notify. I metodi wait-notify forniscono un comodo meccanismo per l'interazione dei thread. Possono anche essere usati per costruire complessi meccanismi di alto livello per l'interazione dei thread."

"Inizierò con un piccolo esempio. Supponiamo di avere un programma per un server che deve eseguire varie attività create dagli utenti attraverso un sito Web. Gli utenti possono aggiungere varie attività in momenti diversi. Le attività richiedono molte risorse, ma il nostro server non -core può farcela. Come dovremmo eseguire le attività sul server?"

"In primo luogo, creeremo un gruppo di thread di lavoro, tanti quanti sono i core del processore. Ogni thread sarà in grado di funzionare sul proprio core: i thread non interferiranno tra loro e i core del processore non stare fermo".

"In secondo luogo, creeremo un oggetto coda in cui verranno aggiunte le attività degli utenti. Diversi tipi di attività corrisponderanno a oggetti diversi, ma tutti implementeranno l'interfaccia Runnable in modo che possano essere eseguiti."

"Potresti farmi un esempio di un oggetto compito?"

"Controlla:"

Una classe che calcola n fattoriale quando viene chiamato il metodo run()
class Factorial implements Runnable
{
 public int n = 0;
 public long result = 1;

 public Factorial (int n)
 {
  this.n = n;
 }

 public void run()
 {
  for (int i = 2; i <= n; i++)
   result *= i;
 }
}

"Fin qui tutto bene."

"Fantastico. Allora esaminiamo come dovrebbe apparire un oggetto in coda. Cosa puoi dirmi a riguardo?"

"Deve essere thread-safe. Viene caricato con oggetti attività da un thread che li riceve dagli utenti, quindi le attività vengono raccolte dai thread di lavoro."

"Sì. E se per un po' restiamo senza compiti?"

"Quindi i thread di lavoro dovrebbero aspettare fino a quando non ce ne saranno altri."

"Esatto. Ora immagina che tutto questo possa essere costruito in un'unica coda. Dai un'occhiata:"

Una coda di attività. Se non ci sono attività, il thread si addormenta e attende che ne appaia una:
public class JobQueue
{
 ArrayList jobs = new ArrayList();

 public synchronized void put(Runnable job)
 {
  jobs.add(job);
  this.notifyAll();
 }

 public synchronized Runnable getJob()
 {
  while (jobs.size() == 0)
   this.wait();

  return jobs.remove(0);
 }
}

"Abbiamo un metodo getJob che controlla se l'elenco delle attività è vuoto. Il thread va quindi a dormire (attende) finché qualcosa non appare nell'elenco."

"Esiste anche il metodo put , che consente di aggiungere una nuova attività all'elenco. Non appena viene aggiunta una nuova attività, viene chiamato il metodo notifyAll . La chiamata a questo metodo risveglia tutti i thread di lavoro che si sono addormentati all'interno del metodo getJob."

"Ricordi di nuovo come funzionano i metodi di attesa e notifica?"

"Il metodo wait viene chiamato solo all'interno di un blocco sincronizzato, su un oggetto mutex. Nel nostro caso: this. Inoltre, accadono due cose:

1) Il filo si addormenta.

2) Il thread rilascia temporaneamente il mutex (finché non si sveglia).

"Successivamente, altri thread possono entrare nel blocco sincronizzato e acquisire lo stesso mutex."

"Il metodo notifyAll può anche essere chiamato solo all'interno del blocco sincronizzato di un oggetto mutex. Nel nostro caso: questo. Inoltre, accadono due cose:"

1) Tutti i thread in attesa su questo oggetto mutex vengono risvegliati.

2) Una volta che il thread corrente esce dal blocco sincronizzato, uno dei thread risvegliati acquisisce il mutex e continua il suo lavoro. Quando rilascia il mutex, un altro thread risvegliato acquisisce il mutex, ecc.

"È molto simile a un autobus. Entri e vuoi pagare il biglietto, ma non c'è l'autista. Quindi ti «addormenti». Alla fine l'autobus è pieno, ma non c'è ancora nessuno a cui dare il biglietto. Poi l'autista arriva e dice: «Ciao, prego». E questo è l'inizio di…».

"Confronto interessante. Ma cos'è un autobus?"

"Julio ha spiegato questo. C'erano queste cose strane usate nel 21° secolo."