The

"Bună, Amigo!"

„Vreau să mă aprofundez cu tine în ceea ce privește așteptarea-notificare. Metodele de așteptare-notificare oferă un mecanism convenabil pentru ca firele de execuție să interacționeze. De asemenea, pot fi folosite pentru a construi mecanisme complexe de nivel înalt pentru interacțiunea firelor.”

„Voi începe cu un mic exemplu. Să presupunem că avem un program pentru un server care trebuie să îndeplinească diverse sarcini create de utilizatori printr-un site web. Utilizatorii pot adăuga diverse sarcini în momente diferite. Sarcinile necesită resurse mari, dar serverul nostru 8 procesorul -core poate face față. Cum ar trebui să realizăm sarcinile pe server?"

„În primul rând, vom crea un grup de fire de lucru, atâtea câte nuclee de procesor există. Fiecare fir de execuție va putea rula pe propriul nucleu: firele de execuție nu vor interfera unele cu altele, iar nucleele de procesor nu vor stai inactiv.”

„În al doilea rând, vom crea un obiect coadă în care vor fi adăugate sarcinile utilizatorilor. Diferite tipuri de sarcini vor corespunde diferitelor obiecte, dar toate vor implementa interfața Runnable pentru a putea fi rulate.”

„Ați putea să-mi dați un exemplu de obiect de sarcină?”

"Verifică:"

O clasă care calculează n factorial atunci când este apelată metoda 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;
 }
}

"Până acum, bine."

„Genial. Atunci să examinăm cum ar trebui să arate un obiect din coadă. Ce poți să-mi spui despre el?”

„Trebuie să fie sigur pentru fire. Este încărcat cu obiecte de activitate de un fir care le primește de la utilizatori, iar apoi sarcinile sunt preluate de firele de lucru.”

"Da. Și dacă rămânem fără sarcini pentru un timp?"

„Atunci firele de lucru ar trebui să aștepte până sunt mai multe.”

"Așa este. Acum imaginați-vă că toate acestea pot fi construite într-o singură coadă. Verificați:"

O coadă de sarcini. Dacă nu există sarcini, atunci firul adoarme și așteaptă să apară 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);
 }
}

„Avem o metodă getJob care verifică dacă lista de sarcini este goală. Apoi firul intră în somn (așteaptă) până când apare ceva în listă.”

"Există și metoda put , care vă permite să adăugați o nouă sarcină la listă. De îndată ce este adăugată o nouă sarcină, este apelată metoda notifyAll . Apelarea acestei metode trezește toate firele de lucru care au adormit în cadrul metodei getJob."

„Îți amintești din nou cum funcționează metodele de așteptare și notificare?”

„Metoda wait este apelată doar în interiorul unui bloc sincronizat, pe un obiect mutex. În cazul nostru: acesta. Mai mult, se întâmplă două lucruri:

1) Firul adoarme.

2) Firul eliberează temporar mutexul (până când se trezește).

„După aceea, alte fire pot intra în blocul sincronizat și pot obține același mutex”.

" Metoda notifyAll poate fi, de asemenea, apelată numai în interiorul blocului sincronizat al unui obiect mutex. În cazul nostru: aceasta. Mai mult, se întâmplă două lucruri:"

1) Toate firele care așteaptă pe acest obiect mutex sunt trezite.

2) Odată ce firul curent iese din blocul sincronizat, unul dintre firele trezite dobândește mutex-ul și își continuă lucrul. Când eliberează mutexul, un alt fir trezit dobândește mutexul etc.

„Seamănă foarte mult cu un autobuz. Intri și vrei să-ți plătești tariful, dar nu există șofer. Așa că „adormi”. În cele din urmă, autobuzul este împachetat, dar încă nu are cui să-i dea tariful. Apoi șoferul sosește și spune: „Plătiți, vă rog”. Și acesta este începutul...”

"Interesanta comparatie. Dar ce este un autobuz?"

"Julio a explicat asta. Au fost aceste lucruri ciudate folosite în secolul 21."