Documente Academic
Documente Profesional
Documente Cultură
Bazele multithreading.......................................................................................................................... 2
Clasa Thread i interfaa Runnable....................................................................................................... 4
Crearea unui fir de execuie ................................................................................................................. 4
Interfaa Runnable ........................................................................................................................... 4
mbuntiri aduse exemplului ........................................................................................................ 7
Motenirea unui Thread .................................................................................................................. 9
Crearea mai multor fire de execuie................................................................................................... 11
Cnd se ncheie un fir de execuie? ................................................................................................ 14
Prioriti n fire de execuie ............................................................................................................... 16
Sincronizarea ..................................................................................................................................... 18
Folosirea metodelor sincronizate ................................................................................................... 19
Blocul synchronized ....................................................................................................................... 21
Comunicarea ntre fire de execuie .................................................................................................... 22
Suspendarea, reluarea i oprirea firelor de execuie .......................................................................... 26
Grupuri de fire de execuie ................................................................................................................ 29
Alte metode................................................................................................................................... 30
Bazele multithreading
Exist doua tipuri distincte de multitasking: unul bazat pe procese i al doilea bazat pe fire de
execuie sau thread-uri. Este important s facem distincia dintre cele dou. Un proces este, n esen,
un program ce se afl n execuie. De aceea multitasking bazat pe procese nseamn faptul c dou sau
mai multe programe ruleaz n acelai timp. De exemplu, compilatorul Java va permite crearea unor noi
programe, n timp ce folosii un editor de text, sau navigai pe Internet.
n cazul multitasking bazat pe thread-uri, acesta este cea mai mic unitate ce va fi programat
de dispecer. Aceasta nseamn c un program poate realiza mai multe lucruri simultan. De exemplu
aplicaia de mail Outlook, sau un client de mail va permite si trimiterea/primirea de mail-uri in timp ce
compunei un nou mail, sau verificai lista de prioriti dintr-o zi, sau cutai un anumit mail, etc.
Avantajul principal al multithreading este c permite scrierea unor programe foarte eficiente,
deoarece se elimin spaiile inutile temporale prezente n multe programe, atunci cnd acesta este idle.
Majoritatea dispozitivelor I/O sunt mai lente dect CPU. De aceea programul va petrece majoritatea
execuiei ateptnd informaia de la aceste dispozitive. n acest timp, n cazul folosirii multithreading, se
pot executa instruciuni care s realizeze alte sarcini. De exemplu, n timp ce un program trimite un fiier
pe Internet, alt parte a programului poate citi de la tastatura, urmtoarea bucat ce va fi trimis.
Un fir de execuie este o parte dintr-un proces executat secvenial, o serie de instruciuni ce vor
fi executate secvenial. Folosirea a dou sau mai multe thread-uri duce la execuia n paralel a acestor
serii de instruciuni n cadrul unui proces. Mai jos avem reprezentat modul de funcionare al unui proces
pe un singur thread.
Un fir de execuie se poate afla n mai multe stri. Poate fi running, adic n execuie. Poate fi
ready to run, n clipa n care dispecerul i va asigura timpul n CPU. Un thread aflat n execuie poate fi
suspendat, ceea ce nseamn c este oprit pentru o perioad. Poate fi reluat, mai trziu, adic va intra n
starea de resumed. Un thread poate fi blocat n momentul cnd ateapt o resurs. Un thread poate fi
terminat atunci cnd execuia se ncheie i nu va mai fi reluat.
Figura de mai jos reprezint strile unui fir de execuie.
Descriere
Toate procesele au cel puin un fir de execuie ce se numete firul principal sau main thread.
Acesta se va executa nainte celorlalte eventual fire de execuie ce vor fi create ulterior.
n cadrul acestei metode, vei defini codul ce constituie noul fir de execuie. Metoda run() poate
apela alte metode, folosi alte clase, i declara variabile ca orice metod. Diferena major este c run()
reprezint entry point pentru un nou thread din cadrul procesului. Acest thread i ncheie execuia
atunci cnd se iese din funcia run (cu return).
Modul de lucru este urmtorul:
1. Se creeaz o clas ce implementeaz interfaa Runnable.
2. Se instaniaz un obiect de tip Thread, ntr-una din metodele din acea clas ( chiar si in
constructorul clasei ). Constructorul unui Thread va fi:
Thread(Runnable threadOb)
3. Odat creat, noul obiect de tip Thread, nu va porni pn cnd nu apelm metoda start(). n
principiu apelul metodei start() nseamn apelul metodei run(). Apelul metodei start este:
void start()
System.out.print(".");
try
{
Thread.sleep(100);
}
catch(InterruptedException exc)
{
System.out.println("Main thread interrupted.");
}
} while (mt.count != 10);
System.out.println("Main thread ending.");
Prima dat, clasa MyThread implementeaz Runnable. Aceasta nseamn c un obiect de tipul
MyThread va fi folosit pentru a crea un thread i deci, va fi parametrul unui constructor Thread.
n interiorul metodei run(), exist o bucl while, ce contorizeaz de la 0 la 9. Metoda
Thread.sleep(500); are ca scop, suspendarea firului de execuie curent timp de 500 de
milisecunde.
n clasa DemoThread, n metoda main(), se creeaz un nou obiect, de tip MyThread care va fi
apelat ulterior de newThrd:
MyThread mt = new MyThread("Child #1");
Thread newThrd = new Thread(mt);
// incepe noul fir de execute
newThrd.start();
Obiectul mt este folosit pentru a crea un Thread, iar acest lucru este posibil pentru c MyThread
implementeaz Runnable . Execuia unui thread ncepe cu start. Pe firul principal de execuie,
instruciunile vor fi rulate ca i cnd start() ar fi o metod obinuit. Firul principal de execuie va rula o
bucl while n care ateapt ca mt.count s ajung la 10. Acest lucru se va ntmpla, datorit faptului
c n thread-ul secundar count crete.
Iat rezultatul rulrii acestui program:
Main thread starting.
.Child #1 starting.
....In Child #1, count is 0
.....In Child #1, count is 1
.....In Child #1, count is 2
.....In Child #1, count is 3
.....In Child #1, count is 4
.....In Child #1, count is 5
.....In Child #1, count is 6
.....In Child #1, count is 7
.....In Child #1, count is 8
.....In Child #1, count is 9
Child #1 terminating.
Main thread ending.
Setarea numelui unui thread, dup ce acesta a fost creat, se face cu setName():
final void setName(String threadName)
do
{
System.out.print(".");
try
{
Thread.sleep(100);
}
catch(InterruptedException exc)
{
System.out.println("Main thread interrupted.");
}
} while (mt.count != 10);
System.out.println("Main thread ending.");
}
}
Modificrile majore sunt: mutarea obiectului de tip Thread n cadrul clasei MyThread i pornirea
acestui fir din constructorul clasei MyThread.
Motenirea unui Thread
Implementarea interfeei Runnable este un mod de a crea o clas ce poate instania fire. A doua
ar fi extinderea unei clase numit Thread. Atunci cnd o clas extinde Thread, va trebui s suprascrie
metoda run(), care este un entry point pentru noul fir. De asemenea trebuie s apeleze start() pentru
ca noul fir s i nceap execuia.
Pornind de la exemplul anterior, putem transforma acesta aplicnd motenirea lui Thread:
1. Se schimba declaraia lui MyThread ca s extind Thread:
class MyThread extends Thread
{
.
Variabila thread nu mai are nici o utilitate, din moment ce MyThread este un Thread.
3. Se schimb constructorul lui MyThread ca s arate astfel:
// construirea unui fir de execuie.
MyThread(String name)
{
Apelul lui super(name); este n fapt, apelul unui Thread cu parametru un String:
Thread(String name);
Cele trei fire de execuie vor rula n paralel cu firul principal, acesta ateptnd ca cele trei sa i ncheie
execuia. O diagram a acestei funcionaliti este n figura de mai jos:
Aceast metod returneaz true, dac firul de execuie, pentru care a fost apelat, nc ruleaz,
i false altfel. Pentru a verifica funcionalitatea isAlive(), vom modifica versiunea clasei
ThreeChildThreads.
class ThreeChildThreads {
public static void main(String args[])
{
System.out.println("Main thread starting.");
//cream trei fire de execuie
MyThread mt1 = new MyThread("Child #1");
MyThread mt2 = new MyThread("Child #2");
MyThread mt3 = new MyThread("Child #3");
//care vor fi automat lansate
//in bucla while, thread-ul principal
//ateapt ca toate firele sa se ncheie
do
{
System.out.print(".");
try
{
Thread.sleep(100);
}
catch(InterruptedException exc)
{
System.out.println("Main thread interrupted.");
}
} while (mt1.thread.isAlive() || mt2.thread.isAlive() ||
mt3.thread.isAlive() );
System.out.println("Main thread ending.");
}
}
Dup cum se poate observa, acest program este la fel ca i cel anterior, cu o modificare: condiia
din while se bazeaz pe aceast funcie, oferind robustee aplicaiei.
Cel de-al doilea mod de a atepta ca un fir s i ncheie execuia este join():
final void join() throws IntrerruptedException
Aceast metod va impune ateptarea terminarea firului pentru care a fost apelat. Numele
provine de la un concept de crearea a unui thread, lansarea a acestuia i ateptarea terminrii lui pentru
ca cel care a creat firul s se uneasc cu acesta. Exist forme adiionale ale acestei funcii, prin care se
permite specificarea unui numr de milisecunde, sau nanosecunde, timp de ateptare ca acel fir s se
ncheie. Mai jos este exemplul pentru aceast funcie:
class ThreeChildThreads
{
public static void main(String args[])
{
System.out.println("Main thread starting.");
//cream trei fire de execuie
MyThread mt1 = new MyThread("Child #1");
MyThread mt2 = new MyThread("Child #2");
MyThread mt3 = new MyThread("Child #3");
//care vor fi automat lansate
//in bucla while, thread-ul principal
//ateapt ca toate firele sa se ncheie
try
{
mt1.thread.join();
System.out.println("Child 1 joined.");
mt2.thread.join();
System.out.println("Child 2 joined.");
mt3.thread.join();
System.out.println("Child 3 joined.");
}
catch(InterruptedException exc)
{
System.out.println("Main thread interrupted.");
}
System.out.println("Main thread ending.");
}
}
Exemplul urmtor demonstreaz folosirea a dou fire de prioritate diferit. Metoda run()
conine o bulc while care contorizeaz iteraii. Aceast bulc se oprete cnd numrul de iteraii
depete 1000000 sau variabila stop a fost setat pe true. Iniial stop este setat pe false, dar primul
thread care termin de iterat, o va seta pe true. Evident aceasta va face ca i al doilea thread s ncheie
bucla. La fiecare parcurgere a buclei, variabila string, currentName, este comparat cu firul aflat n
execuie. Dac este diferit, se realizeaz modificarea acestei variabile. Aceasta permite vizualizarea
accesrii CPU a unui anumit thread.
class Priority implements Runnable
{
int count;
Thread thread;
static boolean stop = false;
static String currentName;
//Constructorul unui nou fir
//acesta nu pornete thread-ul
Priority(String name)
{
thread = new Thread(this, name);
count = 0;
currentName = name;
}
// incepe execuia unui nou fir
public void run()
{
System.out.println(thread.getName() + " starting.");
do
{
count++;
if(currentName.compareTo(thread.getName()) != 0)
{
currentName = thread.getName();
System.out.println("In " + currentName);
}
} while(stop == false && count < 10000000);
stop = true;
System.out.println("\n" + thread.getName() +
" terminating.");
}
}
class PriorityDemo
{
public static void main(String args[])
{
Priority mt1 = new Priority("High Priority");
Priority mt2 = new Priority("Low Priority");
// mt1 primete o prioritate mai mare dect mt2
mt1.thread.setPriority(Thread.NORM_PRIORITY+2);
mt2.thread.setPriority(Thread.NORM_PRIORITY-2);
//pornesc ambele tread-uri
mt1.thread.start();
mt2.thread.start();
//ateptarea terminrii ambelor thread-uri
try
{
mt1.thread.join();
mt2.thread.join();
}
catch(InterruptedException exc)
{
System.out.println("Main thread interrupted.");
}
System.out.println("\nHigh priority thread counted to " +
mt1.count);
System.out.println("Low priority thread counted to " +
mt2.count);
}
}
In High Priority
In High Priority
In Low Priority
In High Priority
In High Priority
In Low Priority
In High Priority
High Priority terminating.
Low Priority terminating.
High priority thread counted to 10000000
Low priority thread counted to 1194749
n acest mod putem observa c pentru acest exemplu, thread-ul cu prioritate mai mare a avut
majoritatea timpului CPU. Acest rezultat va depinde de sistemul de operare i maina pe care va rula.
Sincronizarea
Atunci cnd folosim mai multe fire de execuie, este necesar uneori, s coordonm fluxul logic,
acest proces numindu-se sincronizare. Cel mai simplu motiv pentru aceast sincronizare, este ca dou
sau mai multe fire s aib acces la o resurs comun, dar doar un singur fir s acceseze la un moment
dat acea resurs. De exemplu scrierea ntr-un fiier, efectuat din dou fire de execuie trebuie
controlat. Aceasta se realizeaz astfel: un fir este pus n stare de ateptare pn cnd firul care are
acces la resurs termin aciunea, urmnd ca firul suspendat s i reia execuia.
Sincronizarea n Java este realizat prin intermediul conceptului de monitor, ce controleaz
accesul la un obiect. Monitorul funcioneaz implementnd conceptul de blocare. Cnd un obiect este
blocat de un fir de execuie, nici un alt fir de execuie nu are acces la acel obiect. Cnd firul de execuie
elibereaz acel lock, obiectul devine disponibil pentru celelalte fire de execuie.
Toate obiectele n Java au un monitor. Aceast caracteristic este implementat n limbaj. De
aceea toate obiectele pot fi sincronizate. Acest lucru se va realiza prin cuvntul cheie synchronized i alte
cteva metode pe care toate obiectele le au. Exist dou metode prin care se poate sincroniza fluxul
logic al unui program.
Acest exemplu este compus din trei clase. Prima i anume SumArray conine metoda
sumArray ce realizeaz nsumarea elementelor dintr-un ir. Acest ir este pasat ca parametru al
metodei. A doua clas este, MyThread cea care se ocup de firul de execuie propriu zis. Firul de
execuie va apela metoda sumArray prin intermediul obiectului sa de tip SumArray. Acesta este
declarat static n clasa MyThread. Pe metoda de run() a firului de execuie se va rula aceast metoda de
nsumare a elementelor unui ir. n clasa SyncDemo, n metoda main() este creat un ir i anume a, i
dou fire de execuie ce au ca parametru acelai ir. Cu alte cuvinte, aceeai metod
sa.sumArray(a); va fi apelat n ambele fire, crend o concuren de acces la suma rezultat. Suma
nu va fi ns afectat, deoarece firele vor executa succesiv metoda sumArray .
Rezultatul este:
Child #1 starting.
Running total for Child
Child #2 starting.
Running total for Child
Running total for Child
Running total for Child
Running total for Child
Running total for Child
Sum for Child #1 is 15
Child #1 terminating.
Running total for Child
Running total for Child
Running total for Child
Running total for Child
Sum for Child #2 is 15
Child #2 terminating.
#1 is 1
#1
#1
#1
#1
#2
is
is
is
is
is
3
6
10
15
1
#2
#2
#2
#2
is
is
is
is
3
6
10
15
Aceasta nseamn c execuia a avut loc conform ateptrilor i sumele calculate sunt cele
prevzute. Dar dac eliminm cuvntul cheie synchronized din cadrul metodei, iat ce obinem:
Child #2 starting.
Running total for Child
Child #1 starting.
Running total for Child
Running total for Child
Running total for Child
Running total for Child
Running total for Child
Running total for Child
Running total for Child
Running total for Child
Running total for Child
Sum for Child #1 is 29
Child #1 terminating.
Sum for Child #2 is 29
Child #2 terminating.
#2 is 1
#1
#2
#1
#2
#1
#2
#1
#1
#2
is
is
is
is
is
is
is
is
is
1
3
5
8
11
15
19
24
29
Aici object este referina ctre obiectul de sincronizat. Un bloc sincronizat asigur faptul c
apelul unei metode membr a obiectului object, va avea loc ntr-un monitor al firului de execuie
apelant. De exemplu putem reedita programul mai sus considernd c metoda a fost declarat fr
cuvnt cheie synchronized:
int sumArray(int nums[])
n acest fel se asigur un calcul predictiv al sumei format din elementele lui sa.
Ultimele dou forme ale metodei semnific ateptarea pn cnd apare o notificare sau pn
cnd o perioada de timp, dat de parametrii, trece. Mai jos avem formele funciilor de notificare:
final void notify( )
final void notifyAll( )
n acest exemplu exist trei clase, TicTac, MyThread, ThreadsDemo. Prima clasa, conine
dou metode sincronizate:
synchronized void tic(boolean running)
synchronized void tac(boolean running)
Dac parametrul transmis nu este nc false (mai am drept s rulez metoda) atunci afiez Tic i
dau dreptul lui Tac, urmnd s atept pn cnd thread-ul ce ruleaz Tac, s m ntiineze c a rulat n
monitor. Dac parametrul running este false, atunci anun cellalt fir de execuie i prsesc metoda.
Acelai lucru are loc i pentru metoda tac, evident referitor la thread-ul Tic.
Clasa MyThread, ce implementeaz interfaa Runnable, reprezint cele dou fire de execuie
suport pentru metodele tic() i tac(). n firul de execuie pentru Tic se apeleaz de cinci ori metoda tic()
cu parametru true, i ultima dat cu parametru false, pentru a permite deblocarea firului care executa
tac(). Acelai lucru este valabil viceversa pentru Tac.
n clasa ThreadsDemo se creeaz cele dou fire de execuie, se lanseaz i se ateapt
terminarea lor. Iat ce se ntmpl la rularea programului:
Tic
Tic
Tic
Tic
Tic
Tac
Tac
Tac
Tac
Tac
Dac modificm ns, metodele tac() i tic() eliminnd metodele de comunicare i anume
Totui folosirea acestora este ngrdit, de unele probleme cauzate de suspend() n trecut. De
exemplu, dac un thread obine un lock pe o zon critic, i el este suspendat, acele lock-uri sunt eterne.
n acest timp, alte fire de execuie ateapt deblocarea lor. Metoda resume() este de asemenea
nvechit. Nu cauzeaz probleme, dar nu poate fi folosit fr metoda suspend(). De asemenea metoda
stop() a fost considerat nvechit.
Urmtorul exemplu reprezint un mod n care se pot folosi aceste funcii:
class MyThread implements Runnable
{
Thread thread;
//volatile adic variabila poate fi
//modificata de alte parti ale programului
//cum ar fi un thread
volatile boolean suspended;
volatile boolean stopped;
MyThread(String name)
{
thread = new Thread(this, name);
suspended = false;
stopped = false;
thread.start();
}
// entry point pentru un fir de execuie
public void run()
{
System.out.println(thread.getName() + " starting.");
try
{
for(int i = 1; i < 1000; i++)
{
System.out.print(i + " ");
//din 10 in 10 are loc o pauza
if((i%10)==0)
{
System.out.println();
Thread.sleep(250);
}
Clasa MyThread, definete dou variabile booleane, suspended i stopped. Metoda run()
conine blocul sincronizat ce verific variabila suspended. Dac este true atunci metoda wait() este
apelat i suspend execuia firului. Metoda care stabilete valoarea variabilei la true, este
mysuspend(). Pentru a relua firul, avem myresume(), care seteaz variabila suspended pe true i
apeleaz notify(), pentru a relua execuia firului. Ultima metod este mystop() i conine paii care duc la
oprirea firului de execuie. n clasa SuspendDemo, metodele descrise sunt apelate n alternan pentru
a opri/relua execuia firului de cteva ori. Rezultatul rulrii acestui program este:
My Thread starting.
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
Suspending thread.
Resuming thread.
41 42 43 44 45 46 47 48
51 52 53 54 55 56 57 58
61 62 63 64 65 66 67 68
71 72 73 74 75 76 77 78
Suspending thread.
Resuming thread.
81 82 83 84 85 86 87 88
91 92 93 94 95 96 97 98
101 102 103 104 105 106
111 112 113 114 115 116
Stopping thread.
My Thread exiting.
Main thread exiting.
49
59
69
79
50
60
70
80
89 90
99 100
107 108 109 110
117 118 119 120
currentGroup.enumerate(listOfThreads);
for (int i = 0; i < numThreads; i++)
{
System.out.println("Thread #" + i + " = "
+ listOfThreads[i].getName());
}
}