MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Programare multithreading

Majoritatea limbajelor de programare susțin o funcționalitate importantă precum multithreading-ul, iar Java nu face excepție în acest sens. Cu ajutorul multithreading-ului, putem aloca în aplicație mai multe fire de execuție, care vor efectua diverse sarcini simultan.

Dacă avem, de exemplu, o aplicație grafică ce trimite o solicitare către un server sau citește și procesează un fișier mare, fără multithreading, interfața grafică ar fi blocată pe durata executării sarcinii.

Datorită firelor de execuție, putem delega trimiterea solicitării sau orice altă sarcină care poate dura mult timp într-un fir separat. De aceea, majoritatea aplicațiilor reale pe care le utilizăm sunt aproape de neconceput fără multithreading.

Clasa Thread

În Java, funcționalitatea unui fir de execuție separat este oferită de clasa Thread. Pentru a crea un nou fir, trebuie să creăm un obiect al acestei clase. Dar toate firele nu sunt create de la sine. Când programul pornește, începe să ruleze firul principal al acestuia. Din acest fir principal se generează toate celelalte fire fiice.

Cu ajutorul metodei statice Thread.currentThread(), putem obține firul de execuție curent:

public static void main(String[] args) {
       
   Thread t = Thread.currentThread(); // obținem firul principal
   System.out.println(t.getName()); // main
}

Implicit, numele firului principal va fi main.

Pentru gestionarea firului, clasa Thread oferă și o serie de alte metode. Cele mai utilizate dintre ele sunt:

  • getName(): returnează numele firului
  • setName(String name): setează numele firului
  • getPriority(): returnează prioritatea firului
  • setPriority(int priority): setează prioritatea firului. Prioritatea este unul dintre factorii cheie pentru alegerea firului de către sistem din mulțimea de fire pentru execuție. În această metodă, se transmite ca parametru o valoare numerică a priorității - de la 1 la 10. Implicit, firului principal i se atribuie o prioritate medie - 5
  • isAlive(): returnează true dacă firul este activ
  • isInterrupted(): returnează true dacă firul a fost întrerupt
  • join(): așteaptă finalizarea firului
  • run(): definește punctul de intrare în fir
  • sleep(): suspendă firul pentru un număr specificat de milisecunde
  • start(): pornește firul, apelând metoda sa run()

Putem afișa toate informațiile despre fir:

public static void main(String[] args) {
       
   Thread t = Thread.currentThread(); // obținem firul principal
   System.out.println(t); // main
}

Ieșirea în consolă:

Thread[main,5,main]

Primul main reprezintă numele firului (ce poate fi obținut prin t.getName()), valoarea 5 reprezintă prioritatea firului (de asemenea, poate fi obținută prin t.getPriority()), iar ultimul main reprezintă numele grupului de fire căruia îi aparține firul curent - implicit tot main (poate fi obținut prin t.getThreadGroup().getName()).

Dezavantajele utilizării firelor de execuție

În continuare, vom analiza cum să creăm și să utilizăm firele de execuție. Acest lucru este destul de simplu. Cu toate acestea, la crearea unei aplicații multithreading, trebuie să ținem cont de o serie de factori care pot afecta negativ funcționarea aplicației.

Pe unele platforme, pornirea de noi fire poate încetini funcționarea aplicației. Acest lucru poate fi important dacă performanța aplicației este critică.

Pentru fiecare fir, se creează propria sa stivă de memorie, unde sunt plasate toate variabilele locale și alte date legate de execuția firului. Astfel, cu cât se creează mai multe fire, cu atât se utilizează mai multă memorie.

Trebuie să ne amintim că, în orice sistem, dimensiunea memoriei utilizate este limitată. În plus, multe sisteme pot avea o limită a numărului de fire. Dar chiar dacă nu există o astfel de limitare, există întotdeauna o limitare naturală sub forma vitezei maxime a procesorului.

← Lecția anterioară Lecția următoare →