Bună! Astăzi vom continua să vorbim despre multithreading. Să examinăm clasa Thread și ce fac câteva dintre metodele sale. Când am studiat anterior metodele de clasă, de obicei am scris doar acest lucru: <numele metodei> -> <ce face metoda>.
Acest lucru nu va funcționa cu
Să ne amintim exemplul din lecția anterioară:
Metoda
Metoda

Thread
metodele lui :) Au o logică mai complexă pe care nu o veți putea da seama fără câteva exemple.
Metoda Thread.start().
Să începem prin a ne repeta. După cum probabil vă amintiți, puteți crea un fir făcând ca clasa dvs. să moștenească clasaThread
și suprascriind run()
metoda. Dar nu va începe de la sine, desigur. Pentru a face acest lucru, numim start()
metoda obiectului nostru. 
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("Thread executed: " + getName());
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.start();
}
}
}
Notă: Pentru a începe un thread, trebuie să apelațistart()
metoda specială mai degrabă decâtrun()
metoda! Aceasta este o eroare ușor de făcut, mai ales când începeți să studiați multithreading. În exemplul nostru, dacă apelațirun()
metoda de 10 ori în loc destart()
, veți obține acest lucru:
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.run();
}
}
}
Uitați-vă la rezultatele programului nostru: Fir executat: Fir-0 Fir executat: Fire-1 Fire executat: Thread-2 Thread executat: Thread-3 Thread executat: Thread-4 Thread executat: Thread-5 Thread executat: Thread-6 Thread executat: Thread-7 Thread executat: Thread-8 Thread executat: Thread-9 Uită-te la ordinea de ieșire: Totul se întâmplă în ordine perfectă. Ciudat, nu? Nu suntem obișnuiți cu asta, pentru că știm deja că ordinea în care sunt pornite și executate firele este determinată de un intelect superior din interiorul sistemului nostru de operare: planificatorul de fire. Poate doar am avut noroc? Desigur, nu este vorba despre noroc. Puteți verifica acest lucru rulând programul de încă câteva ori. Problema este că apelulrun()
metoda direct nu are nimic de-a face cu multithreading. În acest caz, programul va fi executat pe firul principal, același fir care execută metoda main()
. Pur și simplu imprimă succesiv 10 rânduri pe consolă și atât. 10 fire nu au fost începute. Deci, amintiți-vă acest lucru în viitor și verificați-vă în mod constant. Dacă doriți ca run()
metoda să fie apelată, apelați start()
. Să mergem mai departe.
Metoda Thread.sleep().
Pentru a suspenda execuția firului curent pentru un timp, folosimsleep()
metoda. 
sleep()
ia un număr de milisecunde ca argument, ceea ce indică timpul necesar pentru a pune firul în stare de repaus.
public class Main {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(3000);
System.out.println(" - How long did I sleep? \n - " + ((System.currentTimeMillis()-start)) / 1000 + " seconds");
}
}
Ieșire consolă: - Cât timp am dormit? - 3 secunde Notă: metoda sleep()
este statică: dorește firul curent. Adică cel în curs de executare. Iată un alt punct important: un fir adormit poate fi întrerupt. În acest caz, programul aruncă un InterruptedException
. Vom lua în considerare un exemplu mai jos. Apropo, ce se întâmplă după ce firul se trezește? Va continua să fie executat chiar de unde a rămas? Nu. După ce un fir se trezește, adică timpul trecut ca argument pentru Thread.sleep()
a trecut, acesta trece la rulabilstat. Dar, acest lucru nu înseamnă că programatorul de fire îl va rula. Este posibil să dea preferință unui alt fir care nu dorm și să permită firului nostru proaspăt trezit să-și continue activitatea puțin mai târziu. Asigurați-vă că rețineți acest lucru: a te trezi nu înseamnă a continua munca imediat!
Metoda Thread.join().

join()
suspendă execuția firului curent până când se termină un alt fir. Dacă avem 2 fire, t1
și t2
, și scriem
t1.join()
atunci t2
nu va începe până nu t1
își va termina munca. Metoda join()
poate fi folosită pentru a garanta ordinea de execuție a firelor de execuție. Să luăm în considerare modul în care join()
funcționează metoda în următorul exemplu:
public class ThreadExample extends Thread {
@Override
public void run() {
System.out.println("Thread started: " + getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + getName() + " is finished.");
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
ThreadExample t1 = new ThreadExample();
ThreadExample t2 = new ThreadExample();
t1.start();
/* The second thread (t2) will start running only after the first thread (t1)
is finished (or an exception is thrown) */
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
// The main thread will continue running only after t1 and t2 have finished
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All threads have finished. The program is finished.");
}
}
Am creat o ThreadExample
clasă simplă. Sarcina sa este să afișeze un mesaj că firul a început, să adormi timp de 5 secunde și apoi să raporteze că lucrarea este finalizată. Bucată de tort. Logica principală este în Main
clasă. Uită-te la comentarii: folosim join()
metoda pentru a gestiona cu succes ordinea de execuție a firelor. Dacă vă amintiți cum am început acest subiect, ordinea de execuție este gestionată de planificatorul de fire. Rulează fire la propria discreție: de fiecare dată într-un mod diferit. Aici folosim metoda pentru a garanta că t1
firul de execuție va fi mai întâi pornit și executat, apoit2
thread și numai după aceea va continua firul principal al programului. Trecând peste. În programele reale, veți găsi adesea situații în care va trebui să întrerupeți execuția unui fir. De exemplu, firul nostru rulează, dar așteaptă un anumit eveniment sau o anumită condiție. Dacă apare, atunci firul se oprește. Probabil că ar avea sens dacă ar exista un fel de stop()
metodă. Dar nu este atât de simplu. Cândva, Java avea de fapt o Thread.stop()
metodă și permitea întreruperea unui fir. Dar a fost ulterior eliminat din biblioteca Java. Îl puteți găsi în documentația Oracle și puteți vedea că este marcat ca depreciat. De ce? Pentru că a oprit firul fără să facă altceva. De exemplu, firul ar putea să lucreze cu date și să schimbe ceva. Apoi, în mijlocul lucrării sale, a fost tăiat brusc și fără ceremonie de stop()
metodă. Fără o închidere adecvată, nici eliberarea de resurse, nici măcar gestionarea erorilor - nu a existat nimic din toate acestea. Pentru a exagera puțin, stop()
metoda pur și simplu a distrus totul în calea ei. Era ca și cum ai trage cablul de alimentare de la priză pentru a opri computerul. Da, poți obține rezultatul dorit. Dar toată lumea știe că, după câteva săptămâni, computerul nu îți va mulțumi că ai tratat-o așa. De aceea, logica de întrerupere a firelor de execuție s-a schimbat în Java și acum folosește o interrupt()
metodă specială.
Metoda Thread.interrupt().
Ce se întâmplă dacăinterrupt()
metoda este apelată pe un fir? Exista 2 posibilitati:
- Dacă obiectul a fost în starea de așteptare, de exemplu, din cauza metodelor
join
sausleep
, atunci așteptarea va fi întreruptă și programul va arunca unInterruptedException
. - Dacă firul de execuție era într-o stare de funcționare, atunci
interrupted
steagul boolean va fi setat pe obiect.
Thread
clasa are boolean isInterrupted()
metoda. Să revenim la exemplul de ceas care a fost într-o lecție din cursul de bază. Pentru comoditate, am simplificat ușor:
public class Clock extends Thread {
public static void main(String[] args) throws InterruptedException {
Clock clock = new Clock();
clock.start();
Thread.sleep(10000);
clock.interrupt();
}
public void run() {
Thread current = Thread.currentThread();
while (!current.isInterrupted())
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("The thread was interrupted");
break;
}
System.out.println("Tick");
}
}
}
În acest caz, ceasul este pornit și începe să ticăie în fiecare secundă. În a 10-a secundă, întrerupem firul ceasului. După cum știți deja, dacă firul pe care încercăm să-l întrerupem se află într-una dintre stările de așteptare, rezultatul este un InterruptedException
. Aceasta este o excepție verificată, astfel încât să o putem prinde cu ușurință și să ne executăm logica pentru a finaliza programul. Și tocmai asta am făcut. Iată rezultatul nostru: Tick Tick Tick Tick Tick Tick Tick Tick Firul a fost întrerupt. Aceasta se încheie introducerea noastră la Thread
cele mai importante metode ale clasei. Noroc!
GO TO FULL VERSION