Sunteți pe pagina 1din 87

INTRODUCERE

JavaTM este un limbaj de programare de nivel nalt,


dezvoltat de JavaSoft, companie n cadrul firmei Sun
Microsystems. Caracteristicile principale ale limbajului sunt:
simplitate, obinut prin eliminarea suprancrcrii operatorilor,
motenirii multiple, robustee, obinut prin eliminarea pointerilor
pentru programator, administrarea automat a memoriei, complet
orientat pe obiecte. Uurina n ceea ce priveste programarea n
reea mbinat cu securitate ridicat, neutraliatatea din punct de
vedere arhitectural precum i portabililtatea, independent de
platforma de lucru, aceeai aplicaie rulnd, far nici o modificare,
pe sisteme diferite cum ar fi Windows, UNIX, Linux sau
Macintosh, lucru care aduce economii substaniale firmelor care
dezvolt aplicaii pentru Internet, deoarece programele Java pot fi
att interpretate, ct i compilate. Codul de octei obinut la
compilare este interpretat de mediul Java i de aceea poate fi rulat
pe orice platform care folosete mediul de execuie JVM
maina virtual Java. Limbajul JavaTM este creat iniial dup C i
C++, de aceea trecerea de la C, C++ la Java TM se face relativ uor.
Limbajul JavaTM permite programarea cu fire de execuie
(multitheaded) mult mai clar i mai sigur, att pentru aplicaii pe un
singur calculator, ct i pentru cele din Internet, Intranet sau reele
de telefonie mobi. Limbajul JavaTM pare s fie cea mai reuit
alegere, n caliate de platform pentru studierea programrii
concurente.
Lucarea dat este adresat studenilor cu specializarea
Calculatoare de la Facultatea CIM. Fiecare din cele patru pri ale
lucrrii conine material teoretic cu exemple de programe. La
sfritul fiecrei pri se propune cte o lucrare de laborator. Pentru
a fi admis la lucrarea de laborator studentul trebuie s rspund
corect la ntrebrile teoretice, puse de profesor, la tema dat.
Lucrarea de laborator se va considera susinut n cazul cnd
3

studentul prezint rularea corect a programului i raportul. Darea


de seam pentru fiecare lucrare de laborator include: foaia de titlu,
tema, scopul lucrrii, coninutul problemei propuse spre rezolvare,
descrierea noiunilor teoretice utilizate pentru rezolvarea
problemei, listingul programului, rezultatele obinute n urma
lansrii programului pe calculator, bibliografia utilizat. Raportul
trebuie sa fie prezentat la control n form electronic fiier de tip
*.doc sau *.java cu documentarea respectiv a lucrrii, i tiprit pe
foi n formatul A4.
Spre rezolvare se propun cate zece variante de probleme
la fiecare lucrare de laborator. Fiecare student i va alege cte o
problem conform variantei din tabel:
Nr Std
Nr.Lab

Lab. 1
Lab. 2
Lab. 3
Lab. 4

1
1
10
6
3

2
9
2
8
4

3
6
3
4
8

4
2
1
7
5

5
4
7
5
6

6
5
6
10
7

7
7
4
2
9

8
8
9
3
1

9
10
5
9
2

10
3
8
1
10

unde pe orizontal se indic numrul de ordine al studentului din


registru n subgrup. De exemplu, studentul cu nr. 5 va efectua la
lucrrile de laborator problemele 4, 7, 5 i 6 respectiv.

PARTEA 1
1.1 Fire de execuie
1.1.1 Ce este un fir de execuie ?
Firele de execuie fac trecerea de la programarea
secvenial la programarea concurent Un program secvenial
reprezint modelul clasic de program: are un nceput, o secven de
execuie a instruciunilor sale i un sfrit. Cu alte cuvinte, la un
moment dat programul are un singur punct de execuie. Un
program aflat n execuie se numeste proces. Un sistem de operare
monotasking (MS-DOS) nu este capabil sa execute dect un singur
proces la un moment dat, n timp ce un sistem de operare
multitasking (UNIX, Windows) poate rula oricte procese n
acelai timp (concurent), alocnd periodic cuante din timpul de
lucru al CPU fiecrui proces. Am reamintit acest lucru deoarece
noiunea de fir de execuie nu are sens dect n cadrul unui sistem
de operare multitasking. Un fir de execuie este similar unui proces
secvenial n sensul c are un nceput, o secven de execuie i un
sfrit. Diferena ntre un fir de execuie i un proces const n
faptul c un fir de execuie nu poate rula independent, ci trebuie s
ruleze n cadrul unui proces.

a)

b)
Fig.1 Fire de execuie n cadrul
unui program
Program cu un fir de execuie;
Program cu mai multe fire de execuie

n fig. 1 se indic locul firelor de execuie n cadrul programului.


Definiie
Un fir de execuie este o succesiune secvenial de
instruciuni care se execut n cadrul unui proces.
Un program i poate defini ns nu doar un fir de execuie, ci
oricte, ceea ce nseamn c n cadrul unui proces se pot executa
simultan mai multe fire de execuie, permind execuia concurent
a sarcinilor independente ale acelui program. Un fir de execuie
poate fi asemnat cu o versiune redus a unui proces, ambele
rulnd simultan i independent pe o structur secvenial de
execuie a instruciunilor sale. De asemenea execuia simultan a
firelor de execuie n cadrul unui proces este similar cu execuia
concurent a proceselor: sistemul de operare va aloca ciclic cuante
din timpul procesorului fiecrui fir de execuie pn la terminarea
lor. Din acest motiv firele de execuie mai sunt numite i procese
uoare. Care ar fi ns deosebirile ntre un fir de execuie i un
proces? n primul rnd, deosebirea major const n faptul c firele
de execuie nu pot rula dect n cadrul unui proces. O alt deosebire
rezult din faptul c fiecare proces are propria sa memorie (propriul
su spaiu de adrese), iar la crearea unui nou proces (fork) este
realizat o copie exact a procesului printe : cod + date; la crearea
unui fir de execuie nu este copiat dect codul procesului printe;
toate firele de execuie au deci acces la aceleai date, datele
procesului original. Astfel, un fir de execuie mai poate fi privit i
ca un context de execuie n cadrul unui proces printe. Firele de
execuie sunt utile n multe privine, ns uzual ele sunt folosite
pentru executarea unor operaii consumatoare de timp fr a bloca
procesul principal: calcule matematice, asteptarea eliberrii unei
resurse, acestea realizndu-se de obicei n fundal.

1.1.2 Concurena thread-urilor


Fr a intra ntr-o discuie pe teme hardware, este bine de
spus c procesoarele calculatoarelor pot executa doar o instruciune
la un moment dat. De ce spunem c thread-uri diferite se execut n
acelai timp? Spunnd simultan, nu nseamn numai lucruri n
medii diferite. Pe o main multiprocesor, thread-urile pot exista pe
procesoare diferite n acelai timp fizic, aceasta meninndu-se
valabil chiar dac procesoarele sunt pe calculatoare diferite
conectate ntr-o reea. Dar i pe o main cu un singur procesor,
thread-urile pot mpri acelai procesor, rulnd ntr-o manier
ntreesut, competiia pentru timpii CPU crend iluzia c ele se
execut simultan. Aceast iluzie pare real cnd 30 de imagini
distincte pe secund captate de ochiul uman sunt percepute ntr-un
flux continuu de imagine. Comutarea ntre thread-uri are i ea un
pre: consum timp CPU pentru ca acesta s nghee starea unui
thread i s dezghee starea unui alt thread (schimbare de context).
Dac thread-urile concurente sunt executate pe acelai procesor i
toate execut calcule, atunci timpul total de execuie nu va lua mai
mult dect timpul de execuie al unui program secvenial care
realizeaz acelai lucru. Din moment ce ntr-un sistem
monoprocesor thread-urile concur la timpul procesor cum este
posibil creterea vitezei sistemului? Aceasta se realizeaz prin
ntreesarea diferitelor faze ale diferitelor thread-uri. Multe task-uri
pot fi segmentate logic n tipuri de faze: faza de calcul i faza I/O.
Faza de calcul necesit atenia maxima din partea CPUului prin utilizarea diferitelor metode de calcul. Faza de I/O
(intrare/ieire) necesit atenie maxim din partea perifericelor
(imprimante, hard discuri, plci de reea, etc) i n aceste situaii
procesorul este n general liber, ateptnd ca perifericul s-i
termine sarcina. Creterea vitezei este obinut prin ntreesarea
fazelor. n timp ce un thread se afl ntr-o faz de I/O ateptnd ca o
secven de date s fie ncrcat de pe hard disk, un thread cu o
7

faz de calcul poate ocupa procesorul i cnd ajunge la o faz I/O,


cellalt thread (care tocmai a terminat faza I/O proprie) poate
ncepe s utilizeze CPU-ul.
1.1.3 Crearea unui fir de execuie
Ca orice alt obiect n Java, un fir de execuie este o
instan a unei clase. Firele de execuie definite de o clas vor avea
acelai cod, prin urmare, i aceeai secven de instruciuni.
Crearea unei clase care s defineasc fire de excuie poate fi fcut
prin dou modaliti:
1. prin extinderea clasei Thread
2. prin implementarea interfeei Runnable
Orice clas ale crei instane vor fi executate ntr-un fir de execuie
trebuie declarat ca fiind Runnable. Aceasta este o interfa care
conine o singur metod, i anume metoda run. Asadar, orice clas
ce descrie fire de execuie va conine o metod run() n care este
implementat codul ce va fi executat de firul de execuie. Interfaa
Runnable este conceput ca fiind un protocol comun pentru
obiectele care doresc s execute un cod pe durata existenei lor
(care reprezint fire de execuie). Cea mai important clas care
implementeaz interfaa Runnable este clasa Thread. Clasa Thread
implementeaz un fir de execuie generic care, implicit, nu face
nimic. Cu alte cuvinte, metoda run nu conine nici un cod. Orice fir
de execuie este o instan a clasei Thread sau a unei subclase a sa.
1.1.3.1 Extinderea clasei Thread
Cea mai simpl metod de a crea un fir de execuie, care
s realizeze ceva, este extinderea clasei Thread i supradefinirea
metodei run a acesteia. Formatul general al unei astfel de clase
este:
8

public class SimpleThread extends Thread {


public SimpleThread(String nume) {
super(nume);
//apelez constructorul superclasei Thread
}

public void run() {


//codul executat de firul de execuie
}

Prima metod a clasei este constructorul, care primete ca argument


un ir ce va reprezenta numele firului de execuie creat n
momentul cnd constructorul este apelat.
SimpleThread t = new SimpleThread("Java");
//creeaza un fir de execuie cu numele Java

n cazul n care nu vrem s dam nume firelor de execuie pe care le


crem, atunci putem renuna la definirea acestui constructor i s
rmnem doar cu constructorul implicit, fr argumente, care
creeaz un fir de execuie fr nici un nume. Ulterior acesta poate
primi un nume cu metoda setName(String).
Evident, se pot defini i ali constructori, acetia fiind utili
cnd vrem s trimitem diveri parametri firului de execuie.
A doua metod este metoda run, "inima" oricrui fir de execuie n
care scriem efectiv codul pe care trebuie s-l execute firul de
execuie. Un fir de execuie creat nu este automat pornit, lansarea
sa n execuie se realizeaz prin metoda start, definit de
asemenea n clasa Thread.
SimpleThread t = new SimpleThread("Java");
t.start();
//creeaz i lanseaz un fir de execuie

S considerm n continuare un exemplu n care definim un fir de


execuie ce afieaz numerele ntregi dintr-un interval cu un anumit
pas. Firul de execuie este implementat de clasa Counter.
class Counter extends Thread {
//clasa care definete firul de execuie
private int from, to, step;

public Counter(int from, int to, int step) {


this.from = from;
this.to = to;
this.step = step;
}

public void run() {


for(int i = from; i <= to; i += step)
System.out.print(i + " " );
}

public class TestCounter {


//clasa principal
public static void main(String args[]) {
Counter cnt1, cnt2;
cnt1 = new Counter(0, 10, 2);
//numar de la 0 la 100 cu pasul 5
cnt2 = new Counter(100, 200, 10);
//numar de la 100 la 200 cu pasul 10
cnt1.start();
cnt2.start();
//pornim firele de execuie
//ele vor fi distruse automat la terminarea lor
}
}

Gndind secvenial, s-ar crede c acest program va afia prima data


numerele de la 0 la 100 cu pasul 5, apoi numerele de la 100 la 200
cu pasul 10, ntruct primul apel este ctre contorul cnt1, deci
rezultatul afiat pe ecran ar trebui s fie: 0 5 10 15 20 25 30 35 40
45 50 55 60 65 70 75 80 85 90 95 100 100 110 120 130 140 150
160 170 180 190 200.
n realitate ns, rezultatul obinut va fi o intercalare de valori
produse de cele dou fire de execuie ce ruleaz simultan. La rulri
diferite se pot obine rezultate diferite deoarece timpul alocat
10

fiecrui fir de execuie poate s nu fie acelai, el fiind controlat de


procesor ntr-o manier "aparent" aleatoare: 0 100 5 110 10 120
15 130 20 140 25 150 160 170 180 190 200 30 35 40 45
50 55 60 65 70 75 80 85 90 95 100

1.1.3.2 Implementarea interfeei Runnable


Ce facem ns cnd dorim s crem o clas care
instaniaz fire de execuie, iar aceasta are deja o superclas, tiind
c n Java nu este permis motenirea multipl?
class FirExecuie extends Printe, Thread //ilegal !

n acest caz nu mai putem extinde clasa Thread, ci trebuie s


implementm direct n clasa noastr interfaa Runnable. Clasa
Thread implementeaza ea nsi interfaa Runnable i, din acest
motiv, la extinderea ei obinem o implementare implicit a
interfeei. Asadar, interfaa Runnable permite unei clase s fie
active, fr a extinde clasa Thread.
Interfaa Runnable se gsete n pachetul java.lang i este
definit astfel:
public interface Runnable {
pulic abstract void run( );
}

Prin urmare, o clas care instaniaz fire de execuie prin


implementarea interfeei Runnable trebuie obligatoriu s
implementeze metoda run. Formatul general al unei clase care
implementeaz interfaa Runnable este:
public class SimpleThread implements Runnable {
private Thread simpleThread = null;
public SimpleThread() {
if (simpleThread == null) {
simpleThread = new Thread(this);
simpleThread.start();
}
public void run() {

11

//codul executat de firul de execuie

Spre deosebire de modalitatea anterioar, se pierde ns tot suportul


oferit de clasa Thread pentu crearea unui fir de execuie. Simpla
instaniere a unei clase care implementeaz interfaa Runnable nu
creeaz nici un fir de execuie. Din acest motiv crearea firelor de
execuie prin instanierea unei astfel de clase trebuie fcut explicit.
Cum se realizeaz acest lucru? n primul rnd, trebuie declarat un
obiect de tip Thread ca variabil membr a clasei respective. Acest
obiect va reprezenta firul de execuie propriu-zis al crui cod se
gsete n clasa noastr:
private Thread simpleThread = null;

Urmtorul pas este instanierea i iniializarea firului de execuie.


Acest lucru se realizeaz ca pentru orice alt obiect prin
instruciunea new, urmat de un apel la un constructor al clasei
Thread, ns nu la oricare dintre acetia. Trebuie apelat
constructorul care s primeasc drept argument o instan a clasei
noastre. Dup creare, firul de execuie poate fi lansat printr-un apel
la metoda start. (Aceste operaiuni sunt scrise de obicei n
constructorul clasei noastre pentru a fi executate la iniializarea
unei instane, dar pot fi scrise oriunde n corpul clasei sau chiar n
afara ei)
simpleThread = new Thread( this );
simpleThread.start();
Specificarea argumentului this n constructorul

clasei Thread
determin crearea unui fir de execuie care la lansarea sa va cuta
n clasa noastr metoda run i o va executa. Acest constructor
accept ca argument orice instan a unei clase "Runnable". Asadar,
metoda run nu trebuie apelat explicit, acest lucru realizndu-se
automat la apelul metodei start().
Apelul explicit al metodei run nu va furniza nici o eroare, ns
aceasta va fi executat ca orice alt metod, deci nu ntr-un fir de
execuie. Sa rescriem acum exemplul anterior (afiarea numerelor
12

ntregi dintr-un interval cu un anumit pas), folosind interfaa


Runnable. Vom vedea c implementarea interfeei Runnable
permite o flexibilitate sporit n lucrul cu fire de execuie.
Varianta 1 (standard)
Crearea firului de execuie se realizeaz n constructorul clasei
Counter:
class Counter implements Runnable {
private Thread counterThread = null;
private int from, to, step;
public Counter(int from, int to, int step) {
this.from = from;
this.to = to;
this.step = step;
if (counterThread == null) {
counterThread = new Thread(this);
counterThread.start();
}
}
public void run() {
for(int i = from; i <= to; i += step)
System.out.print(i + " " );
}
}
public class TestThread2 {
public static void main(String args[]) {
Counter cnt1, cnt2;
//lansez primul fir de execuie (prin constructor)
cnt1 = new Counter(0, 100, 5);
//lansez al doilea fir de execuie (prin constructor)
cnt2 = new Counter(100, 200, 10);
}
}

Varianta 2
Crearea firului de execuie se realizeaz n afara clasei Counter:
class Counter implements Runnable {
private int from, to, step;

13

public Counter(int from, int to, int step) {


this.from = from;
this.to = to;
this.step = step;
}
public void run() {
for(int i = from; i <= to; i += step)
System.out.print(i + " " );
}

}
public class TestThread2 {
public static void main(String args[]) {
Counter cnt1, cnt2;
cnt1 = new Counter(0, 100, 5);
cnt2 = new Counter(100, 200, 10);
new Thread( cnt1 ).start();
//lansez primul fir de execuie
new Thread( cnt2 ).start();
//lansez al doilea fir de execuie
}
}

1.2. Ciclul de via al unui fir de execuie


Fiecare fir de execuie are propriul su ciclu de via: este creat,
devine activ prin lansarea sa n execuie i, la un moment dat, se
termin. n continuare vom vedea mai ndeaproape strile n care se
poate gsi un fir de execuie. Diagrama de mai jos din fig.2
ilustreaz generic aceste stri precum i metodele care provoac
tranziia dintr-o stare n alta:

14

Fig.2 Strile firului de execuie

Aadar, un fir de execuie se poate gsi n una din urmtoarele


patru stri:
1. Starea "New Thread"
Un fir de execuie se gsete n aceast stare imediat dup crearea
sa, cu alte cuvinte dup instanierea unui obiect din clasa Thread
sau dintr-o subclas a sa.
Thread counterThread = new Thread ( this );
//counterThread se gsete n starea New Thread

2. Starea "Runnable"
Dup apelul metodei start un fir de execuie va trece n starea
"Runnable", adic se gsete n execuie.
counterThread.start();
//counterThread se gsete n starea Runnable
Metoda start realizeaz urmatoarele operaiuni necesare

rulrii
firului de execuie: aloc resursele sistem necesare, planific firul
de execuie la CPU pentru a fi lansat, apeleaz metoda run a
obiectului reprezentat de firul de execuie.

15

3. Starea "Not Runnable"


Un fir de execuie ajunge n aceast stare n una din urmatoarele
situaii:
- este "adormit" prin apelul metodei sleep;
- a apelat metoda wait, ateptnd ca o anumit condiie
s fie satisfcut;
- este blocat ntr-o operaie de intrare/ieire.
"Adormirea" unui fir de execuie
Metoda sleep este o metod static a clasei Thread care provoac
o pauz n timpul rulrii firului curent aflat n execuie, cu alte
cuvinte l "adoarme" pentru un timp specificat. Lungimea acestei
pauze este specificat n milisecunde i chiar nanosecunde.
public static void sleep( long millis )
throws InterruptedException
public static void sleep( long millis, int nanos )
throws InterruptedException
ntruct poate provoca excepii de tipul InterruptedException
apelul acestei metode se face ntr-un bloc de tip try-catch:
try {
Thread.sleep(1000);
//face pauz de o secund
} catch (InterruptedException e) {
. . .
}.

4. Starea "Dead"
Este starea n care ajunge un fir de execuie la terminarea sa. Un fir
de execuie nu poate fi oprit din program printr-o anumit metod,
ci trebuie s se termine n mod natural la terminarea metodei run
pe care o execut. Terminarea unui fir de execuie folosind o
variabil de terminare:
import java.io.*;

16

public class TestThread {


public static void main(String args[]) throws
IOException {
WaitKey thread = new WaitKey();
thread.start();
System.in.read();//astept apsarea tastei Enter
thread.running = false;
System.out.println("S-au scurs " + thread.sec
+ " secunde");
}
}
class WaitKey extends Thread {
public int sec = 0;
public boolean running = true;
//variabila de terminare
public void run() {
while ( running ) {
try {
Thread.sleep(1000);
sec ++;
} catch(InterruptedException e){
}}}}

1.3 Gruparea firelor de execuie


Gruparea firelor de execuie pune la dispoziie un mecanism
pentru manipularea acestora ca un tot i nu individual. De exemplu,
putem s pornim sau s suspendm toate firele dintr-un grup cu un
singur apel de metod. Gruparea firelor de execuie se realizeaz
prin intermediul clasei ThreadGroup.
Fiecare fir de execuie Java este membru al unui grup, indiferent
dac specificm explicit acest lucru. Afilierea unui fir de execuie
la un anumit grup se realizeaz la crearea sa i devine permanent,
n sensul c nu vom putea muta un fir de execuie dintr-un grup n
altul, dup ce acesta a fost creat. n cazul n care crem un fir de
17

execuie fr a specifica n constructor din ce grup face parte, el va


fi plasat automat n acelai grup cu firul de execuie care l-a creat.
La pornirea unui program Java se creeaz automat un obiect de tip
ThreadGroup cu numele main, care va reprezenta grupul tuturor
firelor de execuie create direct din program i care nu au fost
ataate explicit altui grup. Cu alte cuvinte, putem s ignoram
complet plasarea firelor de execuie n grupuri i s lsm sistemul
s se ocupe cu aceasta, adunndu-le pe toate n grupul main. Exist
situaii cnd programul creeaz multe fire de execuie, iar gruparea
lor poate uura substanial manevrarea lor. Crearea unui fir de
execuie i plasarea lui ntr-un grup (altul dect cel implicit) se
realizeaz prin urmtorii constructori ai clasei Thread:
public Thread(ThreadGroup group, Runnable target)
public Thread(ThreadGroup group, String name)
public Thread(ThreadGroup group, Runnable target,
String name)

Fiecare din aceti costructori creeaz un fir de execuie, l


iniializeaz i l plaseaz ntr-un grup specificat ca argument. n
exemplul urmtor vor fi create dou grupuri, primul cu dou fire de
execuie, iar al doilea cu trei fire de execuie:
ThreadGroup
Thread p1 =
Thread p2 =
ThreadGroup
Thread c1 =
Thread c2 =
Thread c3 =

grup1 = new ThreadGroup("Producatori");


new Thread(grup, "Producator 1");
new Thread(grup, "Producator 2");
grup2 = new ThreadGroup("Consumatori");
new Thread(grup, "Consumator 1");
new Thread(grup, "Consumator 2");
new Thread(grup, "Consumator 3");

Pentru a afla crui grup aparine un anumit fir de execuie putem


folosi metoda getThreadGroup a clasei Thread. Un grup poate
avea ca printe un alt grup, ceea ce nseamn c firele de execuie
pot fi plasate ntr-o ierarhie de grupuri, n care rdcina este grupul
implicit main, ca n fig. 3 de mai jos:
18

Fig. 3 Gruparea firelor de execuie

Exemplu: listarea firelor de execuie active

public class EnumerateTest {


public void listCurrentThreads() {
ThreadGroup currentGroup =
Thread.currentThread().getThreadGroup();
//aflu numarul firelor de execuie active
int numThreads = currentGroup.activeCount();
//pun intr-un vector referine la firele de exec.
//active
Thread[] listOfThreads = new Thread[numThreads];
currentGroup.enumerate(listOfThreads);
//le afisez pe ecran
for (int i = 0; i < numThreads; i++)
System.out.println("Thread #" + i + " = " +
listOfThreads[i].getName());
}
}

Structura de fire de execuie i grupuri de execuie poate fi


reprezentat n forma urmtoare:

19

MainGrupa{subGrupa1{subGrupa3{Tha, Thb, Thc, Thd},


ThA},subGrupa2{Th1, Th2, Th3},Th1, Th2}.

Lucrare de laborator nr. 1


1. Tema lucrrii:
Crearea i enumerarea thread-urilor
2. Scopul lucrrii:
nsuirea modalitilor de creare a thread-urlilor n Java;
nsuirea metodelor de creare a unui grup de thread-uri;
3. Etapele de realizare:
1) Utilizarea clasei Thread pentru crearea unei clase noi;
2) Utilizarea interfeei Runnable pentru crearea i lansarea
de thread-uri;
3) Utilizarea clasei ThreadGroup pentru crearea grupelor de
thread-uri;
4) Setarea prioritilor thread+urilor cu setPriority(.);
5) Enumerarea thread-urilor i a prioritilor lor cu
enumerate();
6) Utilizare list();
7) Prezentarea lucrrii.
4. Exemplu de realizare:
public class ThreadGroup1 {
public static void main(String[] args) {
ThreadGroup sys =
Thread.currentThread().getThreadGroup();
sys.list(); // (1)
sys.setMaxPriority(Thread.MAX_PRIORITY - 1);
Thread curr = Thread.currentThread();
curr.setPriority(curr.getPriority() + 1);
sys.list(); // (2)
ThreadGroup g1 = new ThreadGroup("g1");
g1.setMaxPriority(Thread.MAX_PRIORITY);

20

Thread t = new Thread(g1, "A");


t.setPriority(Thread.MAX_PRIORITY);
g1.list(); // (3)
g1.setMaxPriority(Thread.MAX_PRIORITY - 2);
g1.setMaxPriority(Thread.MAX_PRIORITY);
g1.list(); // (4)
t = new Thread(g1, "B");
t.setPriority(Thread.MAX_PRIORITY);
g1.list(); // (5)
g1.setMaxPriority(Thread.MIN_PRIORITY + 2);
t = new Thread(g1, "C");
g1.list(); // (6)
t.setPriority(t.getPriority() -1);
g1.list(); // (7)
ThreadGroup g2 = new ThreadGroup(g1, "g2");
g2.list(); // (8)
g2.setMaxPriority(Thread.MAX_PRIORITY);
g2.list(); // (9)
for (int i = 0; i < 5; i++)
new Thread(g2, Integer.toString(i));
sys.list(); // (10)
System.out.println("Starting all threads:");
Thread[] all = new Thread[sys.activeCount()];
sys.enumerate(all);
for(int i = 0; i < all.length; i++)
if(!all[i].isAlive())
all[i].start();
}} ///:~

Rezultatul rulrii programului va fi:


(1) ThreadGroup[name=system,maxpri=10]
Thread[main,5,system]
(2) ThreadGroup[name=system,maxpri=9]
Thread[main,6,system]
(3) ThreadGroup[name=g1,maxpri=9]
Thread[A,9,g1]
(4) ThreadGroup[name=g1,maxpri=8]
Thread[A,9,g1]
(5) ThreadGroup[name=g1,maxpri=8]
Thread[A,9,g1]
Thread[B,8,g1]

21

(6) ThreadGroup[name=g1,maxpri=3]
Thread[A,9,g1]
Thread[B,8,g1]
Thread[C,6,g1]
(7) ThreadGroup[name=g1,maxpri=3]
Thread[A,9,g1]
Thread[B,8,g1]
Thread[C,3,g1]
(8) ThreadGroup[name=g2,maxpri=3]
(9) ThreadGroup[name=g2,maxpri=3]
(10)ThreadGroup[name=system,maxpri=9]
Thread[main,6,system]
ThreadGroup[name=g1,maxpri=3]
Thread[A,9,g1]
Thread[B,8,g1]
Thread[C,3,g1]
ThreadGroup[name=g2,maxpri=3]
Thread[0,6,g2]
Thread[1,6,g2]
Thread[2,6,g2]
Thread[3,6,g2]
Thread[4,6,g2]
Starting all threads:
All threads started

5. Probleme propuse:
Realizarea lucrrii de laborator nr. 1 const din 2 probleme.Prima
problem ine de crearea i executarea firelor de execuie. A doua
problem se refer la gruparea i enumerarea firelor de execuie.
Problema nr.1:

22

Scriei un program care creeaz doua fire de execuie.


Ambele fire vor citi datele din acelai
tablou de date mas[] de tipul int, generat
aleatoriu cu dimensiunea 100 i ce cuprinde
valori intre 1 i 100. Primul fir Th1 va
afia: Condiie 1 din tabelul 1.
Al doilea fir Th2 va afia: Condiie 2 din tabelul 1.
Apoi dup terminarea ambelor fire de execuie threadul principal va afia informaia despre
studentul care a efectuat lucrarea dat de
laborator, literele textului vor aprea pe
ecran cu un interval de 100 milisecunde.

Tabelul 1
Condiii la problema nr. 1 l/lab nr. 1
1
2
3
4
5
6
7
8

Condiie 1

Condiie 2

Sumele numerelor pare dou cte


dou ncepnd cutarea i sumarea
de la primul element
Suma poziiilor numerelor pare
dou cte dou ncepnd cu-tarea
i sumarea de la primul element
Sumele numerelor pare dou cate
dou ncepnd cutarea i sumarea
de la primul element
Sumele poziiilor numerelor pare
dou cte dou ncepnd cutarea
i sumarea de la primul element
Sumele produselor numerelor de
pe poziii pare dou cte dou
ncepnd cu primul element
Sumele produselor numerelor de
pe poziii impare dou cte dou
ncepnd cu primul element
Sumele produselor numerelor
pare dou cte dou ncepnd cu
primul element
Sumele produselor numerelor

Sumele numerelor pare dou cte


dou ncepnd cutarea i sumarea
de la ultimul element
Sumele poziiilor numerelor pare
dou cte dou ncepnd cutarea
i sumarea de la ultimul element
Sumele numerelor pare dou cate
dou ncepnd cutarea i sumarea
de la ultimul element
Sumele poziiilor numerelor pare
dou cte dou ncepnd cutarea
i sumarea de la ultimul element
Sumele produselor numerelor de
pe poziii pare dou cte dou
ncepnd cu ultimul element
Sumele produselor numerelor de
pe poziii impare dou cte dou
ncepnd cu ultimul element
Sumele produselor numerelor pare
dou cte dou ncepnd cu ultimul
element
Sumele produselor numerelor

23

9
10

impare dou cte dou ncepnd


cu primul element
Sumele numerelor de pe primele
trei poziii pare
ncepnd cu
primul element
Diferenele produselor nume-relor
de pe poziii impare cte dou
ncepnd cu primul element

impare dou cte dou ncepnd cu


ultimul element
Sumele numerelor de pe poziii
pare cte trei ncepnd cu ultimul
element
Sumele produselor numerelor de
pe poziii impare dou cate dou
ncepnd cu primul element

Problema nr. 2:

De

De creat o structur, conform formulei date


n tabelul 2. De enumerat toate firele de
execuie din grupa principal i subgrupele
ce le conine.
afiat
informaia
despre
numele
firului
de
execuie, numele grupei din care face parte,
prioritatea
sa.
n
paranteze
()
,vezi
tabelul 2, se indic prioritatea fiecrui
Thread.

Tabelul 2
Formula structurii, compus din fire i
grupuri de fire

1
2
3
4
5
6
7
8
9
10

Main{G1{G3{Tha(3), Thb(3), Thc(3), Thd(3)},


ThA(3)},G2{Th1(4), Th2(5), Th3(5)},Th1(7), Th2(7)}
Main{G2{G1{Tha(1), Thb(3), Thc(8), Thd(3)},
ThA(1)},G3{Th1(4), Th2(3), Th3(5)},Th1(3), Th2(6)}
Main{GN{GH{Tha(4), Thb(3), Thc(6), Thd(3)},
ThA(3)},GM{Th1(2), Th2(3), Th3(3)},Th1(8), Th2(3)}
Main{GO{GZ{Tha(1), Thb(3), Thc(3), Thd(7)},
ThA(3)},GF{Th1(5), Th2(3), Th3(9)},Th1(3), Th2()}
Main{GE{GH{Tha(4), Thb(3), Thc(2), Thd(1)},
ThA(3)},GK{Th1(3), Th2(6), Th3(3)},Th1(3), Th2(7)}
Main{G1{G3{Thf(3), Thb(7), Thc(3), Thd(3)},
ThA(3)},G2{Th8(3), Th9(4), Th3(3)},Th1(3), Th2(3)}
Main{ThA(3)},G2{Th1(5), Th2(3), Th33(7)},Th11(3),
Th22(3), G1{G3{Thaa(2), Thbb(3), Thcc(8), Thdd(3)}}
Main{G4{G3{Tha(2), Thb(8), Thc(3), Thd(3), G2{Th1(3),
Th2(3), Th3(3), ThA(3)},Th1(8), Th2(3)}} }
Main{G6{ ThA(3)},Th1(4),24
Th2(3), G2{Th1(2),
G3{Tha(2), Thb(3), Thc(4), Thd(3)}, Th2(3), Th3(3)}}
Main{G7{G3{Tha(6), Thb(3), Thc(6), Thd(3)},
ThA(7)},Th1(6), Th2(3), G2{Th1(7), Th2(3), Th3(3)}}

Condiii la problema nr 2 l / lab. nr. 1


PARTEA 2
2.1 Problema productor / consumator.
Sincronizarea firelor de execuie
Exist numeroase situaii cnd fire de execuie separate,
dar care ruleaz concurent, trebuie s comunice ntre ele pentru a
accesa diferite resurse comune sau pentru a-i transmite dinamic
rezultatele "muncii" lor. Cel mai elocvent scenariu n care firele de
execuie trebuie sa comunice ntre ele este cunoscut sub numele de
problema productorului/consumatorului, n care productorul
genereaz un flux de date care este preluat i prelucrat de ctre
consumator. S considerm de exemplu o aplicaie Java n care un
fir de execuie (productorul) scrie date ntr-un fiier, n timp ce alt
fir de execuie (consumatorul) citete date din acelai fiier pentru a
le prelucra. Sau, s presupunem c productorul genereaz nite
numere i le plaseaz pe rnd ntr-un buffer, iar consumatorul
citete numerele din acel buffer pentru a le interpreta. n ambele
cazuri avem de-a face cu fire de execuie concurente care folosesc
o resurs comun : un fiier, respectiv un vector i, din acest motiv,
ele trebuie sincronizate ntr-o manier care s permit decurgerea
normal a activitii lor. Pentru a nelege mai bine modalitatea de
sincronizare a dou fire de execuie s implementm efectiv o
problem de tip productor/consumator. S considerm urmtoarea
situaie:

Productorul genereaz numerele ntregi de la 1 la 10,


fiecare la un interval neregulat cuprins ntre 0 i 100 de
milisecunde. Pe msur ce le genereaz ncearc s le

25

plaseze ntr-o zon de memorie (o variabil ntreag) de


unde sa fie citite de ctre consumator.
Consumatorul va prelua, pe rnd, numerele generate de
ctre productor i va afia valoarea lor pe ecran.

Pentru a fi accesibil, ambelor fire de execuie, vom ncapsula


variabila ce va conine numerele generate ntr-un obiect descris de
clasa Buffer i care va avea dou metode put (pentru punerea unui
numr n buffer) i get (pentru obinerea numrului din buffer).
Fr a folosi nici un mecanism de sincronizare clasa Buffer arat
astfel:
class Buffer {
private int number = -1;
public int get() {
return number;
}
public void put(int number) {
this.number = number;
}
}

Vom implementa acum clasele Productor i Consumator care vor


descrie cele dou fire de execuie. Ambele vor avea o referin
comun la un obiect de tip Buffer prin intermediul cruia i
comunic valorile.
class Producator extends Thread {
private Buffer buffer;
public Producator(Buffer b) {
buffer = b;
}
public void run() {
for (int i = 0; i < 10; i++) {
buffer.put(i);
System.out.println("Producatorul a pus:\t"+ i);
try {

26

sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { }
}

}
class Consumator extends Thread {
private Buffer buffer;
public Consumator(Buffer b) {
buffer = b;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = buffer.get();
System.out.println("Consumatorul a
primit:\t" + value);
}
}
}
//Clasa principal
public class TestSincronizare1 {
public static void main(String[] args) {
Buffer b = new Buffer();
Producator p1 = new Producator(b);
Consumator c1 = new Consumator(b);
p1.start();
c1.start();
}
}

Dupa cum ne ateptm, rezultatul rulrii acestui program nu va


rezolva nici pe departe problema propus de noi, motivul fiind lipsa
oricrei sincronizri ntre cele dou fire de execuie. Mai precis,
rezultatul va fi ceva de forma:
Consumatorul
Consumatorul
Producatorul
Consumatorul
Consumatorul
Consumatorul

27

a
a
a
a
a
a

primit:
primit:
pus:
primit:
primit:
primit:

-1
-1
0
0
0
0

Producatorul
Producatorul
Producatorul
Producatorul
Producatorul
Producatorul
Producatorul
Producatorul
Producatorul

a
a
a
a
a
a
a
a
a

pus:
pus:
pus:
pus:
pus:
pus:
pus:
pus:
pus:

1
2
3
4
5
6
7
8
9

Ambele fire de execuie acceseaz resursa comun, adic obiectul


de tip Buffer, ntr-o manier haotic i acest lucru se ntmpl din
dou motive :
consumatorul nu ateapt nainte de a citi ca productorul s
genereze un numr i va prelua de mai multe ori acelai
numr.
productorul nu ateapt consumatorul s preia numrul
generat nainte de a produce un altul, n felul acesta
consumatorul va "rata" cu siguran unele numere (n cazul
nostru, aproape pe toate).
Problema care se pune n acest moment este: cine trebuie s se
ocupe de sincronizarea celor dou fire de execuie : clasele
Productor i Consumator sau resursa comun Buffer?
Rspunsul este: resursa comun Buffer, deoarece ea trebuie s
permit sau nu accesul la coninutul su, i nu firele de execuie
care o folosesc. n felul acesta efortul sincronizrii este transferat
de la productor/consumator la un nivel mai jos, cel al resursei
critice.
Activitile productorului i ale consumatorului trebuie
sincronizate la nivelul resursei comune n dou privine:
1. Cele dou fire de execuie nu trebuie s acceseze simultan
buffer-ul; acest lucru se realizeaz prin blocarea obiectului
Buffer atunci cnd este accesat de un fir de execuie, astfel
nct nici un alt fir de execuie s nu-l mai poat accesa.

28

2.

Cele dou fire de execuie trebuie s se coordoneze, adic


productorul trebuie s gseasc o modalitate de a "spune"
consumatorului c a plasat o valoare n buffer, iar
consumatorul trebuie s comunice productorului c a
preluat aceast valoare, pentru ca acesta s poat genera o
alt valoare. Pentru a realiza aceast comunicare, clasa
Thread pune la dispoziie metodele wait, notify,
notifyAll.

Folosind sincronizarea clasa Buffer va arta astfel:


class Buffer {
private int number = -1;
private boolean available = false;
public synchronized int get() {
while (!available) {
try {
wait();
//ateapt producatorul s pun o valoare
} catch (InterruptedException e) { }
}
available = false;
notifyAll();
return number;
}
public synchronized void put(int number) {
while (available) {
try {
wait();
//ateapt consumatorul s preia valoarea
} catch (InterruptedException e) { }
}
this.number = number;
available = true;
notifyAll();
}
}

Rezultatul obinut va fi cel scontat:


Producatorul a pus:

29

Consumatorul
Producatorul
Consumatorul
. . .
Producatorul

a primit: 0
a pus:
1
a primit: 1
a pus:

2.2 Metode de sincronizare a thread-urilor


2.2.1 Blocarea unui obiect (cuvntul cheie synchronized)
Definiie
Un segment de cod ce gestioneaz o resurs comun mai
multor fire de execuie separate i concurente se numete
seciune critic. n Java o seciune critic poate fi un bloc
de instruciuni sau o metod.
Controlul accesului ntr-o seciune critic se face prin cuvntul
cheie synchronized. Platforma Java asociaz un monitor fiecrui
obiect al unui program ce conine seciuni critice care necesit
sincronizare. Acest monitor va indica dac resursa critic este
accesat de vreun fir de execuie sau este liber, cu alte cuvinte
"monitorizeaz" o resurs critic. n cazul n care este accesat, va
"pune un lact" pe aceasta, astfel nct s mpiedice accesul altor
fire de execuie la ea. n momentul cnd resursa este eliberat
"lactul" va fi eliminat pentru a permite accesul altor fire de
execuie.
n exemplul tip productor/consumator de mai sus, seciunile
critice sunt metodele put i get, iar resursa citic comun este
obiectul buffer. Consumatorul nu trebuie s acceseze buffer-ul
cnd productorul tocmai pune o valoare n el, iar productorul nu
trebuie sa modifice valoarea din buffer n momentul cnd aceasta
este citit de ctre consumator.
public synchronized int get() {
...
}
public synchronized void put(int number) {
...

30

S observam c ambele metode au fost declarate cu modificatorul


synchronized. Cu toate acestea sistemul asociaz un monitor unei
instane a clasei Buffer i nu unei metode anume. n momentul n
care este apelat o metod sincron, firul de execuie care a fcut
apelul, va bloca obiectul a crui metod o acceseaz, ceea ce
nseamn c celelalte fire de execuie nu vor mai putea accesa
resursele critice, adic nu vor putea apela nici o metod sincron
din acel obiect. Acesta este un lucru logic, deoarece mai multe
seciuni critice (metode sincrone) ale unui obiect gestioneaz, de
fapt, o singur resurs critic. n exemplul nostru, atunci cnd
productorul apeleaz metoda put pentru a scrie un numr, va
bloca tot obiectul de tip Buffer, astfel ca firul de execuie
consumator nu va avea acces la cealalt metod sincron get, i
reciproc.
public synchronized void put(int number) {
// buffer blocat de productor
...
// buffer deblocat de productor
}
public synchronized int get() {
// buffer blocat de consumator
...
// buffer deblocat de consumator
}

2.2.2 Metodele wait, notify i notifyAll


Obiectul de tip Buffer din exemplu are o variabil membr privat
numit number, n care este memorat numrul pe care l comunic
productorul i din care l preia consumatorul. De asemenea, mai
are o variabil privat logic available, care ne d starea bufferului: dac are valoarea true nseamn c productorul a pus o
valoare n buffer i consumatorul nu a preluat-o nc; dac este
false, consumatorul a preluat valoarea din buffer, dar productorul
31

nu a pus deocamdat alta la loc. Deci, la prima vedere metodele


clasei Buffer ar trebui s arate astfel:
public synchronized int get() {
if (available) {
available = false;
return number;
}
}
public synchronized int put(int number) {
if (!available) {
available = true;
this.number = number;
}
}

Implementate ca mai sus cele dou metode nu vor funciona corect.


Acest lucru se ntmpl, deoarece firele de execuie, dei i
sincronizeaz accesul la buffer, nu se "ateapt" unul pe cellalt.
Situaiile n care metodele get i put nu fac nimic, vor duce la
"ratarea" unor numere de ctre consumator. Aadar, cele dou fire
de execuie trebuie s se atepte unul pe cellalt.
public synchronized int get() {
while (!available) {
//nimic - atept ca variabila s devin true
}
available = false;
return number;
}
public synchronized int put(int number) {
while (available) {
//nimic - atept ca variabila s devin false
}
available = true;
this.number = number;
}

Varianta de mai sus, dei pare, nu este corect. Aceasta, deoarece


implementarea metodelor este "selfish" - cele dou metode i
ateapt n mod egoist condiia de terminare. Ca urmare,
corectitudinea funcionrii va depinde de sistemul de operare, ceea
32

ce reprezint o greeal de programare. Punerea corect a unui fir


de execuie n ateptare se realizeaz cu metoda wait a clasei
Thread, care are trei forme:
void wait( )
void wait( long timeout )
void wait( long timeout, long nanos ).
apelul metodei wait, firul de execuie curent

Dup
elibereaz
monitorul asociat obiectului respectiv i ateapt ca una din
urmtoarele condiii s fie ndeplinit:
un alt fir de execuie informeaz pe cei care "ateapt" la un
anumit monitor s se trezeasc; acest lucru se realizeaz
printr-un apel al metodei notifyAll sau notify.
perioada de ateptare specificat a expirat.
Metoda

wait
poate
InterruptedException,

produce
excepii
de
tipul
atunci cnd firul de execuie care
asteapt (este deci n starea Not Runnable) este ntrerupt din
ateptare i trecut forat n starea Runnable, dei condiia ateptat
nu era nc ndeplinit. Metoda notifyAll informeaz toate firele
de execuie, care sunt n ateptare la monitorul obiectului curent,
ndeplinirea condiiei pe care o ateptau. Metoda notify
informeaz doar un singur fir de execuie. Iat variantele corecte
ale metodelor get i put:
public synchronized int get() {
while (!available) {
try {
wait();
//ateapt productorul s pun o valoare
} catch (InterruptedException e) { }
}
available = false;
notifyAll();
return number;
}
public synchronized void put(int number) {

33

while (available) {
try {
wait();
//ateapt consumatorul s preia valoarea
} catch (InterruptedException e) { }
}
this.number = number;
available = true;
notifyAll();

}}

2.2.3 Bariere
n aplicaiile multithreading este necesar ca anumite thread-uri s
se sincronizeze la un anumit punct. Un exemplu este calculul
paralel n faza, n care toate thread-urile trebuie s-i termine faza
de execuie nainte de a trece toate odat la faza urmtoare. O
barier este un mecanism folosit pentru a sincroniza mai multe
thread-uri. Un thread care ntlnete o barier intr automat n
wait(). Cnd ultimul thread "ajunge" la barier, semnaleaz
(notify()) celorlalte thread-uri, care sunt n ateptare, rezultnd o
"trecere" n grup a barierei. Iat un exemplu n acest sens:
import java.util.*;
class Barrier { // Clasa Barrier sincronizeaz toi
//participanii private
int ParticipatingThreads;
private int WaitingAtBarrier;
public Barrier(int num){ //Constructorul
ParticipatingThreads = num;
WaitingAtBarrier=0;
}
public synchronized void Reached(){
//Metoda bariera
WaitingAtBarrier++;
if(ParticipatingThreads != WaitingAtBarrier){
//Inseamna ca thread-ul nu este ultimul

34

try {

wait(); //Thread-ul este oprit pn ce


//este eliberat
} catch (InterruptedException e) { }

} else { // Acesta a fost ultimul thread activ


notifyAll();
WaitingAtBarrier=0;//le elibereaz pe toate

}
}

Ajungnd la barier toate thread-urile, mai puin ultimul, asteapt


n interiorul metodei synchronized ca ultimul thread ajuns s le
elibereze. Odat ce s-au "trezit" ei intr n competiie pentru
monitor i unul dintre ei l ctig, apoi iese imediat elibernd
monitorul. Celelalte thread-uri continu s concureze pentru
acest monitor i s-l elibereze pn ce ultimul thread face acest
lucru i-n acest moment sunt cu toii "liberi". n acest exemplu am
presupus c se cunoate
dinainte numrul de thread-uri
participante. Dac, ns, acest numr nu este cunoscut dinainte
poate fi aplicat un mecanism de nregistrare a participanilor.
Orice thread care dorete s atepte la barier, mai nti trebuie s
se "nregistreze" incrementnd numarul de thread-uri n ateptare.
De unde tim c toate thread-urile care ateapt la barier s-au
nregistrat? Ce se ntmpl dac toate thread-urile, mai puin unul,
i-au terminat de fcut calculele i ultimul thread (care, probabil,
nici nu a fost planificat s ruleze nc) nu s-a nregistrat nc. Toate
celelalte thread-uri vor trece bariera netiind c trebuie s-l
atepte i pe acesta din urm.
2.2.4 Semafoare
Java are aceast sincronizare cu monitoare, dar poate utiliza
i semafoare. Utilizind monitoare, se poate implementa orice obiect
35

sincronizat dorit, inclusiv semafoare. Iat n continuare un


exemplu de semafor n Java:
class Semaphore {
protected int value;
Semaphore( int initialValue ) {
value = initialValue;
}
Semaphore() {
value = 0;
}
public synchronized void Get() {
while (value<1)
wait();
value--;
}
public synchronized void Put() {
value++;
notify();
}
}

Lucrare de laborator nr. 2


1. Tema lucrrii:
Sincronizarea thread-urilor n Java.
2. Scopul lucrrii:
nsuirea metodelor de sincronizare a thread-urilor
3. Etapele de realizare:
1) Utilizarea clasei Thread pentru crearea unei clase noi;
2) Utilizarea interfeei Runnable pentru crearea i lansarea
de thread-uri;
3) Implementarea clasei productor;
4) Implementarea clasei consummator;
36

5) Sincronizarea thread-urilor prin diferite metode:


a) utiliznd variabile logice;
b) utiliznd monitoare (synchronized).
4. Exemplu de realizare:
Trebuie s includ clasa Productor, Consumator cu
sinhronizarea respectiv
public class Producer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Producer(CubbyHole c, int number) {
cubbyhole = c;
this.number = number;
}
public void run() {
for (int i = 0; i < 10; i++) {
cubbyhole.put(i);
System.out.println("Producer #" +
this.number + " put: " + i);
try {
sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { }
}
}
}
public class Consumer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Consumer(CubbyHole c, int number) {
cubbyhole = c;
this.number = number;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = cubbyhole.get();

37

System.out.println("Consumer #" +
this.number + " got: " + value);
}

}
public class ProducerConsumerTest {
public static void main(String[] args) {
CubbyHole c = new CubbyHole();
Producer p1 = new Producer(c, 1);
Consumer c1 = new Consumer(c, 1);
p1.start();
c1.start();
}
}

5. Probleme propuse:
X productori genereaz aleatoriu F obiecte care sunt
consumate de Y consumatori. De afiat informaia despre
producerea i consumarea obiectelor, mesajele despre cazurile
cnd depozitul e gol sau plin. Toate operaiile se efectueaz
pn cnd fiecare consumator este ndestulat cu Z obiecte.
Dimensiunea depozitului este D. Valorile se iau din tabelul 3.
Tabelul 3
Date iniiale pentru problem la l/lab nr. 3
Nr.
1
2
3
4
5
6
7
8
9

2
3
4
3
2
2
3
4
5

3
4
3
2
5
4
3
2
2

11
2
3
12
3
4
5
4
3

8
5
10
11
12
7
6
5
4

38

Tip Obiecte
Numere pare
Numere impare
Vocale
Consoane
Numere pare
Numere impare
Vocale
Consoane
Numere pare

3
3
5
2
10
Numere impare
* fiecare productor poate produce cte 2 obiecte de fiecare
dat
PARTEA 3

3.1 Applet-uri
3.1.1 Ce este un applet ?
Definiie
Un applet reprezint o suprafa de afiare (container) ce
poate fi inclus ntr-o pagin Web i gestionat printr-un
program Java. Un astfel de program se mai numete
miniaplicaie sau, prin abuz de limbaj, applet.
Codul unui applet poate fi format din una sau mai multe clase. Una
dintre acestea este principal i extinde clasa Applet, fiind clasa ce
trebuie specificat n documentul HTML ce descrie pagina de Web
n care dorim s includem appletul.
Diferena fundamental dintre un applet i o aplicaie const n
faptul c un applet nu poate fi executat independent, ci va fi
executat de browserul n care este ncrcat pagina Web, ce conine
appletul respectiv. O aplicaie independent este executat prin
apelul interpretorului java, avnd ca parametru numele clasei
principale a aplicaiei, clasa principal fiind cea, care conine
metoda main. Ciclul de via al unui applet este complet diferit,
fiind dictat de evenimentele generate de ctre browser la
vizualizarea documentului HTML, ce contine appletul. Pachetul
care ofer suport pentru creearea de appleturi este java.applet.
3.1.2 Crearea unui applet

39

Orice applet este implementat prin crearea unei subclase a clasei


Applet. Ierarhia claselor din care deriv Applet este prezentat n
fig. 4:

Fig. 4 Ierarhia claselor din care provine clasa Applet

Fiind derivat din clasa Container, clasa Applet descrie de fapt


suprafee de afiare, asemenea claselor Frame sau Panel.
Un applet simplu
import java.applet.Applet;
import java.awt.*;
public class AppletSimplu extends Applet {
public void paint(Graphics g) {
g.setFont(new Font("Arial",
Font.BOLD, 16));
g.drawString("Hello", 0, 30);
}
}

Uzual, clasa principal va fi salvat ntr-un fiier cu acelai nume i


extensia .java. Asadar, vom salva clasa de mai sus ntr-un fiier
AppletSimplu.java.

40

Execuia (vizualizarea)
Pentru a vizualiza acest applet trebuie s crem un document
HTML, s-i spunem demo.html, n care s specificm cel puin
urmatoarele informaii:
clasa ce contine codul appletului;
limea i nlimea suprafeei alocate pe pagina Web
<HTML>
<HEAD><TITLE> Un applet simplu </TITLE>
</HEAD>
<APPLET CODE="AppletSimplu.class" WIDTH=100
HEIGHT=50></APPLET>
</HTML>

Vizualizarea acestui document se poate face cu orice browser


(Internet Explorer, Netscape, etc), sau cu utilitarul appletviewer
ce vine n pachetul JDK.
appletviewer demo.html

3.1.3 Structura general a unui applet


import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
public class StructuraApplet extends Applet {
public void init() {
}
public void start() {
}
public void stop() {
}
public void destroy() {

41

}
public void paint(Graphics g) {
}
}

3.1.4 Definirea i folosirea parametrilor


Parametrii sunt pentru appleturi ceea ce argumentele de la linia de
comand sunt pentru aplicaiile independente. Ei permit
utilizatorului s personalizeze aspectul sau comportarea unui applet
fr a-i schimba codul i recompila clasele.
Definirea parametrilor se face n cadrul tagului APPLET din
documentul HTML ce conine appletul i sunt identificai prin
atributul PARAM. Fiecare parametru are un nume, specificat prin
NAME i o valoare, specificat prin VALUE, ca n exemplul de
mai jos:
<APPLET CODE="AppletSimplu.class" WIDTH=100 HEIGHT=50
<PARAM NAME=textAfisat VALUE="Salut">
<PARAM NAME=numeFont VALUE="Times New Roman">
<PARAM NAME=dimFont VALUE=20>
</APPLET>

Folosirea parametrilor primii de ctre un applet se face prin


intermediul metodei getParameter care primete ca argument
numele unui parametru i returneaz valoarea acestuia. n cazul n
care nu exist nici un parametru cu numele specificat, metoda
ntoarce null, caz n care programul trebuie s atribuie o valoare
implicit variabilei n care se dorea citirea respectivului parametru.
S rescriem appletul considerat iniial (AppletSimplu), astfel nct
acesta s afieze textul primit ca parametru, folosind un font cu
numele i dimensiunea specificate de asemenea ca parametri.
import java.applet.Applet;
import java.awt.*;

42

public class AppletSimplu extends Applet

String text, numeFont;


int dimFont;
public void init() {
text = getParameter("textAfisat");
if (text==null)
text="Hello"; // valoare implicit
numeFont = getParameter("numeFont");
if (numeFont==null)
numeFont="Arial";
try {
dimFont =
Integer.parseInt(getParameter("dimFont"));
} catch(NumberFormatException e) {
dimFont = 16;
}
}
public void paint(Graphics g) {
g.setFont(new Font(numeFont, Font.BOLD,dimFont));
g.drawString(text, 20, 20);
}
}

3.2 Folosirea firelor de execuie n appleturi


Fiecare applet aflat pe o pagin Web se execut ntr-un fir de
execuie propriu. Acesta este creat de ctre browser i este
responsabil cu desenarea appletului (apelul metodelor update i
paint) precum i cu transmiterea mesajelor generate de ctre
componentele appletului. n cazul n care dorim s realizm i alte
operaiuni consumatoare de timp, este recomandat s le realizm
ntr-un alt fir de execuie, pentru a nu bloca interaciunea
utilizatorului cu appletul sau redesenarea acestuia.
Structura unui applet care dorete s lanseze un fir de
execuie poate avea dou forme. n prima situaie appletul pornete
43

un fir de execuie la iniializarea sa, iar acesta va rula, indiferent


dac appletul mai este sau nu vizibil, pna la oprirea sa natural
(terminarea metodei run)
import java.applet.Applet;
class AppletThread1 extends Applet implements Runnable
{
Thread appletThread = null;

public void init() {


if (appletThread == null) {
appletThread = new Thread(this);
appletThread.start();
}
}
public void run() {
//codul firului de execuie
}

n cazul n care firul de execuie pornit de applet efectueaz


operaii ce au sens doar dac appletul este vizibil, cum ar fi
animaie, ar fi de dorit ca acesta s se opreasca atunci cnd appletul
nu mai este vizibil (la apelul metodei stop) i s reporneasc atunci
cnd appletul redevine vizibil (la apelul metodei start).
import java.applet.Applet;
public class StructuraApplet extends Applet implements
Runnable {
Thread appletThread = null;
boolean running = false;
public void start() {
//reporneste firul de execuie
if (appletThread == null) {
appletThread = new Thread(this);
running = true;
appletThread.start();
}
}

44

public void stop() {


//oprete firul de execuie
running = false;
appletThread = null;
}
public void run() {
while (running) {
//codul firului de execuie
}
}
}

Un applet este considerat activ imediat dup apelul metodei start


i devine inactiv la apelul metodei stop. Pentru a afla dac un
applet este activ se folosete metoda isActive.
Lucrare de laborator nr. 3
1. Tema lucrrii:
Animaia appleturilor cu ajutorul thread-urilor.
2. Scopul lucrrii:
nsuirea modalitilor de creare a appletelor n Java;
nsuirea metodelor de utilizare a thread-urilor n applete
pentru animare.
3. Etapele de realizare:
1) Utilizarea clasei Thread pentru crearea unei clase noi;
2) Utilizarea interfeei Runnable pentru crearea i lansarea
de thread-uri;
3) Crearea paginii html ce include tagurile applet;
4) Utilizare a parametrilor din applet n executarea
appletului;
45

5) Utilizare a buferizrii duble pentru animaia appletului,


utiliznd thread-urile.
4. Exemplu de realizare:
Textul din parametrii paginii html se va deplasa pe suprafaa
appletului.
import
import
import
import

java.awt.Graphics;
java.awt.Font;
java.awt.Image;
java.awt.Rectangle;

public class ScrollText extends java.applet.Applet


implements Runnable{
int h;
int w;
char separated[];
String s=null;
String hs=null;
String ws=null;
Thread killme=null;
int speed=35;
boolean threadSuspended=false;
int dist;
Image offscreenImage;
Graphics offscreenGraphics;
public void init(){
ws=getParameter("width");
hs=getParameter("height");
if(ws==null){
w=150;
}else{
w=Integer.parseInt(ws);
}
if(hs==null){
h=50;
}else{
h=Integer.parseInt(hs);
}

46

resize(w,h);
s=getParameter("text");
if(s==null)
s="The Java ScrollText at work.";
separated =new char[s.length()];
s.getChars(0,s.length(),separated, 0);
offscreenImage=createImage(w,h);
offscreenGraphics=offscreenImage.getGraphics();
offscreenGraphics.setFont(new
Font("TimesRoman",Font.BOLD,h-2));
}
public void start(){
if(killme==null){
killme=new Thread(this);
killme.start();
}
}
public void stop(){
killme=null;
}
public void run(){
while(killme!=null){
try{
Thread.sleep(speed);
}catch(InterruptedException w){}
scroll();

}
killme=null;
}
synchronized void scroll(){
dist--;
if(dist+((s.length()+1)*(h*5/11))==0)
dist=w;
repaint();
}
public void paint(Graphics g){
Rectangle r=bounds();

47

offscreenGraphics.clearRect(0,0,r.width,
r.height);
offscreenGraphics.drawChars(separated,0,s.length(),
dist,4*h/5);
g.drawImage(offscreenImage,0,0,this);
}
public void update(Graphics g){
paint(g);
}
public boolean mouseDown(java.awt.Event evt, int
x, int y){
if(threadSuspended){
killme.resume();
}else{
killme.suspend();
}
threadSuspended=!threadSuspended;
return true;
}
}

Coninutul fiierului HTML pentru ncrcarea appletului:


<html>
<head><title>ScrollText</title></head>
<body>
<applet code="ScrollText.class"
align="middle" width="150" height="50"
vspase="15" hspase="5"> <p>Ar trebui sa
fie applet </p> </applet><br>
<applet
code=ScrollText.class width=150 height=50>
</applet>
</body>
</html>

48

5. Probleme propuse:
De scris un program pentru realizarea unui applet ce va utiliza
firele de execuie pentru realizarea:
1.
Ceas, ce poate fi setat i restartat prn click ( prefaa
unei emisiuni TV);
2.
Cronometru, cu butoanele start/stop i reset;
3.
Generare de figuri geometrice ce se mic, ce pot fi
create sau distruse apsnd pe butonul ce corespunde
figurii;
4.
Joc de calculator tetris cu 2 figuri de tipul tetris
utiliznd tastatura, n care pot fi 2 participani;
5.
Componenta text ce i schimba umbrele n
dependen de micarea sursei de lumin;
6.
Image loader, applet ce afieaz un ir de imagini, n
timp ce se poate de scris text n TextArea;
7.
Spot publicitar, ce poate fi ntrerupt i repornit, ce
caracterizeaz FCIM;
8.
Spot publicitar ce poate fi ntrerupt i repornit, ce
caracterizeaz Catedra Calculatoare;
9.
Spot publicitar ce poate fi ntrerupt i repornit, ce
caracterizeaz Cursul Java;
10.
Component, ce poate fi intrerupt i repornit, ce
reprezint micarea planetelor n sistemul solar, ca o parte
a unui applet, ce ar permite listarea imaginilor n acelai
timp de ctre utilizator.

49

PARTEA 4
4.1 Ce sunt fluxurile?
Adeseori programele necesit citirea unor informaii care se gsesc
pe o surs extern sau trimiterea unor informaii ctre o destinaie
extern. Informaia se poate gsi oriunde: ntr-un fiier pe disc, n
reea, n memorie sau n alt program i poate fi de orice tip: date
primitive, obiecte, imagini, sunete, etc. Pentru a aduce informaii
dintr-un mediu extern, un progam Java trebuie s deschid un
canal de comunicaie (flux) ctre sursa informaiilor (fiier,
memorie, socket, etc) i s citeasc serial informaiile respective,
cum e artat n fig.5.

Fig. 5. Flux de citire

Similar, un program poate trimite informaii ctre o destinaie


extern deschiznd un canal de comunicaie (flux) ctre acea
destinaie i scriind serial informaiile respective, vezi fig.6.

50

Fig. 6. Flux de scriere

Indiferent de tipul informaiilor, citirea/scrierea informaiilor de


pe/ctre un mediu extern respect urmtorii algoritmi:
Citirea

Scrierea

deschide canal comunicaie


while (mai sunt
informaii) {
citete informaie
}
inchide canal comunicaie

deschide canal comunicaie


while (mai sunt informaii)
{
scrie informaie
}
inchide canal comunicaie

Pentru a generaliza att sursa extern a unor informaii, ct i


destinaia lor sunt vzute ca fiind nite procese care produc,
respectiv consuma informaii:
Un flux este un canal de comunicaie unidirecional ntre dou
procese. Fluxurile sunt canale de comunicaie seriale pe 8 sau 16
bii. Fluxurile sunt unidirecionale, de la productor la consumator.
Clasele i intefeele standard pentru lucu cu fluxuri se gsesc n
pachetele java.io, java.nio.
Metode comune fluxurilor
Superclasele abstracte Reader i InputStream definesc metode
similare pentru citirea datelor:

51

Reader

InputStream

int read()
int read()
int read(char buf[])
int read(byte buf[])
int read(char buf[], int int read(byte buf[], int
offset,int length)
offset,int length)

De asemenea ambele clase pun la dispoziie metode pentru


marcarea unei locaii ntr-un flux, saltul peste un numr de poziii,
resetarea poziiei curente, etc. Superclasele abstracte Writer i
OutputStream sunt de asemenea paralele, definind metode similare
pentru scrierea datelor.
Writer
int
int
int
int

write()
write(char buf[])
write(char buf[],
offset,int length)

OutputStream
int write()
int write(byte buf[])
int write(byte buf[], int
offset,int length)

nchiderea oricrui flux se realizeaz prin metoda close. n cazul


n care aceasta nu este apelat explicit, fluxul va fi automat nchis
de catre colectorul de gunoaie, atunci cnd nu va mai exista nici o
referin la el.Metodele referitoare la fluxuri pot genera excepii de
tipul IOException.

4.1.1 Crearea unui flux


Orice flux este un obiect al clasei ce implementeaz fluxul
respectiv. Crearea unui flux se realizeaz similar cu crearea
obiectelor prin instruciunea new().
Exemple:

52

//crearea unui flux de intrare pe caractere


FileReader n = new
FileReader("fiier_intrare.txt");
//crearea unui flux de ieire pe caractere
FileWriter out = new
FileWriter("fiier_iesire.txt");
//crearea unui flux de intrare pe octeti
FileInputStream n = new
FileInputStream("fiier_intrare.txt");
//crearea unui flux de ieire pe octeti
FileOutputStrem out = new
FileOutputStream("fiier_iesire.txt");

Crearea unui flux primitiv de date care scrie/citete informaii de la


un dispozitiv extern are formatul general:
FluxPrimitiv numeFlux = new
FluxPrimitiv( dispozitiv extern)

Fluxurile de procesare nu pot exista de sine stttoare, ci se


suprapun pe un flux primitiv de citire/scriere a datelor. Din acest
motiv, constructorii claselor pentru fluxurile de procesare nu
primesc ca argument un dispozitiv extern de memorare a datelor, ci
o referin la un flux primitiv responsabil cu citirea/scrierea
efectiv a datelor:
Exemple:
//crearea unui flux de intrare printr-un buffer
BufferedReader n = new BufferedReader(new
FileReader("fiier.in"));
//echivalent cu
FileReader fr = new FileReader("fiier.in");
BufferedReader n = new BufferedReader(fr);
//crearea unui flux de ieire printr-un buffer

53

BufferedWriter out = new BufferedWriter(new


FileWriter("fiier.out")));
//echivalent cu
FileWriter fo = new FileWriter("fiier.out");
BufferedWriter out = new BufferedWriter(fo);

Crearea unui flux pentru procesarea datelor are formatul general:


FluxProcesare numeFlux = new FluxProcesare(
referintaFluxPrimitiv);

In general, fluxurile pot fi grupate n succesiuni orict de lungi:


DataInputStream n =
new DataInputStream(
new BufferedInputStream(
new
FileInputStream("fiier.in")));

4.1.2 Fluxuri pentru lucrul cu fiiere (fluxuri de tip File)


Fluxurile pentru lucrul cu fiiere sunt cele mai uor de neles.
Clasele care implementeaz aceste fluxuri sunt urmtoarele:
FileReader, FileWriter - caractere
FileInputStream, FileOutputStream - octei

Constructorii acestor clase accept ca argument un obiect care s


specifice un anume fiier. Acesta poate fi un ir de caractere, un
obiect de tip File sau un obiect de tip FileDesciptor.
Constructorii clasei FileReader:
public FileReader( String fileName ) throws
FileNotFoundException
public FileReader( File file ) throws
FileNotFoundException

54

public FileReader( FileDescriptor fd )

Constructorii clasei FileWriter:


public
IOException
public
IOException
public
public
boolean append

FileWriter( String fileName ) throws


FileWriter( File file ) throws
FileWriter( FileDescriptor fd )
FileWriter( String fileName,
) throws IOException

Cei mai uzuali constructori sunt cei care primesc ca argument


numele fiierului. Acetia pot provoca excepii de tipul
FileNotFoundException n cazul, n care fiierul cu numele
specificat nu exist. Din acest motiv, orice creare a unui flux de
acest tip trebuie fcut ntr-un bloc try sau metoda n care sunt
create fluxurile respective trebuie s arunce excepiile de tipul
FileNotFoundException sau de tipul superclasei IOException.
Exemplu: un program care copie coninutul unui fiier
n alt fiier:
import java.io.*;
public class Copy {
public static void main(String[] args) throws
IOException {
FileReader n = new
FileReader("in.txt");
FileWriter out = new
FileWriter("out.txt");
int c;
while ((c = in.read()) != -1)
out.write(c);
in.close();

55

out.close();

}
Obs: metoda main arunca excepii IOException
care este superclasa pentru
FileNotFoundException. Aceste excepii nu vor fi
"prinse", dect de nterpretor i va fi afiat
un mesaj de eroare la apariia lor.

Cele mai importante clase din aceast categorie sunt


DataInputStream i DataOutputStream .
4.1.3 Citirea datelor de la tastatur
Uzual, vom dori s folosim metoda readLine pentru citirea datelor
de la tastatur i din acest motiv vom folosi intrarea standard
mpreun cu o clas de procesare care implementeaz metoda
readLine. Exemplul tipic este:
BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in));
System.out.print("Introduceti o linie:");
String linie = stdin.readLine()
System.out.println(linie);

Exemplu: un program care afiseaz liniile introduse de la tastatur


import java.io.*;
public class Echo {
public static void main(String[] args) {
BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in));
String s;
try {
while((s = stdin.readLine()).length() != 0)
System.out.println(s);

56

//Programul se opreste cu o linie vida


} catch(IOException e) {
e.printStackTrace();
}
}

4.1.4 Analiza lexical pe fluxuri (clasa StreamTokenizer)


Clasa StreamTokenizer parcurge un flux de intrare de orice tip i
l mparte n "atomi lexicali". Rezultatul va consta n faptul c n
loc s se citeasc octei sau caractere, se vor citi, pe rnd, atomii
lexicali ai fluxului respectiv.
Printr-un atom lexical se nelege n general:
o
o
o
o
o

un identificator (un ir care nu este ntre ghilimele);


un numr;
un ir de caractere;
un comentariu;
un separator.

Atomii lexicali sunt desprii ntre ei de separatori. Implicit aceti


separatori sunt cei obinuii (spaiu, tab, virgul, punct i virgul),
ns pot fi schimbai prin diverse metode ale clasei.
Constructorii acestei clase sunt:
public StreamTokenizer( Reader r )
public StreamTokenizer( InputStream is )

Identificarea tipului i valorii unui atom lexical se face prin


intermediul variabilelor:
TT_EOF - atom ce marcheaz sfrsitul fluxului

57

TT_EOL TT_NUMBER
TT_WORD
nval
sval
ttype
-

atom ce marcheaz sfrsitul unei linii


- atom de tip numar
- atom de tip cuvnt
valoarea unui atom numeric
irul coninut de un atom de tip cuvnt
tipul ultimului atom citit din flux

Citirea atomilor din flux se face cu metoda nextToken, care


returneaz tipul atomului lexical citit i scrie n variabilele nval sau
sval valoarea corespunztoare atomului. Exemplul tipic de folosire
a unui analizator lexical este citirea unei secvene de numere i
iruri aflate ntr-un fiier sau primite de la tastatur:
//Citirea unei secvene de numere i iruri
import java.io.*;
public class TestTokenizer {
public static void main(String args[]) throws
IOException{
FileInputStream fis = new
FileInputStream("test.dat");
BufferedReader br = new BufferedReader(new
InputStreamReader(fis));
StreamTokenizer st = new StreamTokenizer(br);
int tip = st.nextToken();
//citesc primul atom lexical
while (tip != StreamTokenizer.TT_EOF) {
switch (tip) {
case StreamTokenizer.TT_WORD ://cuvnt
System.out.println(st.sval);
break;
case StreamTokenizer.TT_NUMBER ://numar
System.out.println(st.nval);
}
tip = st.nextToken();//urmtorul atom
}
}
}

58

Asadar, modul de utilizare tipic pentru un analizator lexical este


ntr-o bucl "while" n care se citesc atomii unul cte unul cu
metoda nextToken pn se ajunge la sfritul fluxului (TT_EOF). n
cadrul buclei "while" se afl tipul atomul curent(ntors de metoda
nextToken) i apoi se afl valoarea numeric sau irul de caractere,
corespunztor atomului respectiv. Un exemplu mai simplu de
folosire (dar nepractic) ar fi citirea unui ntreg sau a unui ir de
caractere de la tastatur:
//Citirea unui ntreg de la tastatur
import java.io.*;
public class TestReadIn {
public static void main(String args[]) {
try{
Reader r = new
InputStreamReader(System.in);
StreamTokenizer st = new
StreamTokenizer(r);
System.out.print("n=");
int tip = st.nextToken();
System.out.println("Valoarea lui n este
" + (int)st.nval);
}
catch (IOException e) {}
}
}

4.1.5 Clasa File


Clasa File are un nume neltor, ntruct ea nu se refer doar la
un fiier, ci poate reprezenta fie un fiier anume, fie mulimea
fiierelor dintr-un director. O instan a acestei clase poate s
reprezinte: un fiier sau un director. Specificarea unui fiier/director
se face prin specificarea cii absolute spre acel fiier sau a cii
59

relative fa de directorul curent. Acestea trebuie s respecte


conveniile de specificare a cilor i numelor fiierelor de pe
maina gazd. Utilitatea clasei File const n furnizarea unei
modaliti de a abstractiza dependenele cilor i numelor fiierelor
fa de maina gazd, precun i punerea la dispoziie a unor metode
pentru lucrul cu fiiere i directoare la nivelul sistemului de
operare. Astfel, n aceast clas vom gsi metode pentru testarea
existenei, tergerea, redenumirea unui fiier sau director, crearea
unui director, listarea fiierelor dintr-un director, etc.
Trebuie menionat i faptul c, majoritatea constructorilor
fluxurilor, care permit accesul la fiiere, accept ca argument un
obiect de tip File n locul unui ir ce reprezint numele fiierului
accesat.
File f_in = new File("fiier.txt");
FileInputStream st_in = new FileInputStream(f_in).

4.2 Programarea n reea


4.2.1 Noiuni generale despre reele
Programarea n reea implic trimiterea de mesaje i date ntre
aplicaii ce ruleaz pe calculatoare aflate ntr-o reea local sau
conectate la Internet. Pachetul care ofer suport pentru scrierea
aplicaiilor de reea este java.net. Clasele din acest pachet ofer o
modalitate facil de programare n reea, fr a fi nevoie de
cunotine prealabile referitoare la comunicarea efectiv ntre
calculatoare. Cu toate acestea sunt necesare cteva noiuni
fundamentale referitoare la reele, cum ar fi protocol, adresa IP,
port, socket.

60

4.2.2 Ce este un protocol ?


Un protocol reprezint o convenie de reprezentare a datelor
folosit n comunicarea ntre dou calculatoare. Avnd n vedere
faptul ca orice informaie care trebuie trimis prin reea trebuie
serializat, astfel nct s poat fi transmis secvenial, octet cu
octet, ctre destinaie, era nevoie de stabilirea unor convenii
(protocoale) care s fie folosite att de calculatorul care trimite
datele, ct i de cel care le primete.
Cele mai utilizate protocoale sunt TCP i UDP.
Definiii
TCP (Transport Control Protocol) este un protocol ce
furnizeaz un flux sigur de date ntre dou calculatoare.
Acest protocol asigur stabilirea unei conexiuni permanente
ntre cele dou calculatoare pe parcursul comunicaiei.
UDP (User Datagram Protocol) este un protocol ce trimite
pachete independente de date, numite datagrame, de la un
calculator ctre altul fr a garanta n vreun fel ajungerea
acestora la destinaie. Acest protocol nu stabilete o
conexiune permanent ntre cele dou calculatoare.
4.2.3 Cum este identificat un calculator n reea ?
Orice calculator gazd conectat la Internet este identificat n mod
unic de adresa sa IP (IP este acronimul de la Internet Protocol).
Aceasta reprezint un numr reprezentat pe 32 de bii, uzual sub
forma a 4 octei, cum ar fi de exemplu: 193.226.216.231 i este
numit adresa IP numerica. Corespunztoare unei adrese
numerice exist i o adres IP simbolica, cum ar fi
http://mail.md. De asemenea, fiecare calculator aflat ntr-o
reea local are un nume unic ce poate fi folosit la identificarea
local a acestuia.

61

Clasa

Java

care

reprezint

noiunea

de

adres

IP este

InetAddress.

4.2.4 Ce este un port ?


Un calculator are n general o singura legtur fizic la reea. Orice
informaie destinat unei anumite maini trebuie s specifice
obligatoriu adresa IP a acelei maini. ns pe un calculator pot
exista concurent mai multe procese care au stabilite conexiuni n
reea, ateptnd diverse informaii. Prin urmare, datele trimise ctre
o destinaie trebuie s specifice pe lng adresa IP a calculatorului
i procesul ctre care se ndreapt informaiile respective.
Identificarea proceselor se realizeaz prin intermdiul porturilor.
Un port este un numr de 16 bii, care identific n mod unic
procesele care ruleaz pe o anumit main. Orice aplicaie care
realizeaz o conexiune n reea va trebui s ataeze un numr de
port acelei conexiuni. Valorile pe care le poate lua un numr de port
sunt cuprinse ntre 0 i 65535 (deoarece sunt numere reprezentate
pe 16 bii), numerele cuprinse ntre 0 i 1023 fiind rezervate unor
servicii sistem i, din acest motiv, nu trebuie folosite n aplicaii.
Clase de baz din java.net
Clase din java.net permit comunicare ntre procese folosind
protocoalele TCP: URL,URLConnection, Socket,
ServerSocket, i UDP: DatagramPacket, DatagramSocket,
MulticastSocket.

4.2.5 Lucrul cu URL-uri


Definiie
URL este acronimul pentru Uniform Resource Locator
i reprezint o referin (adres) la o resurs aflat pe
Internet. Aceasta este, n general, un fiier reprezentnd o
pagin Web sau o imagine, ns un URL poate referi i

62

interogri la baze de date, rezultate ale unor comenzi


(programe), etc.
Exemple de URL-uri sunt:

http://java.sun.com,

http://mail.md.

Clasa care permite lucrul cu URL-uri este java.net.URL. Aceasta


are mai muli constructori pentru crearea de obiecte ce reprezint
referine ctre resurse aflate n reea, cel mai uzual fiind cel care
primete ca parametru un ir de caractere. n cazul n care irul nu
reprezint un URL valid, va fi aruncat o excepie de tipul
MalformedURLException.
try {
URL myURL = new URL("http://java.sun.com");
} catch (MalformedURLException e) {
. . .
}

Odata creat, un obiect de tip URL poate fi folosit pentru


o

o
o

aflarea informaiilor despre resursa referit (numele


calculatorului gazd, numele fiierului, protocolul
folosit, etc);
citirea printr-un flux a coninutului fiierului
respective;
conectarea la acel URL pentru citirea i scrierea de
informaii.

4.2.6 Citirea coninutului unui URL


Orice obiect de tip URL poate returna un flux de intrare de tip
InputStream pentru citirea coninutului su. Secvena clasic pentru
aceast operaiune este:

63

//Afiarea paginii index.html de la adresa


www.infoiasi.ro
public class CitireURL {
public static void main(String[] args) throws
IOException{
BufferedReader br = null;
try {
URL resursa = new
URL("http://www.infoiasi.ro");
InputStream n = resursa.openStream();
br = new BufferedReader(new
InputStreamReader(in));
String linie;
while ((linie = br.readLine()) != null) {
//proceseaza linia citita
System.out.println(linie);
}
}catch(MalformedURLException e) {
System.err.println("URL incorect: " + e);
}
finally {
br.close();
}
}
}

Conectarea

la

un

openConnection().

URL

se

realizeaz

prin

metoda

4.2.7 Socket-uri
Definiie
Un socket (soclu) este o abstraciune software folosit
pentru a reprezenta fiecare din cele dou "capete" ale unei
conexiuni ntre dou procese ce ruleaz ntr-o reea.
Fiecare socket este ataat unui port astfel, nct s poat
identifica unic programul cruia i sunt destinate datele.

64

Socket-urile sunt de dou tipuri:


TCP, implementate de clasele Socket i
;
UDP, implementate de clasa DatagramSocket .

ServerSocket
o

O aplicaie de reea ce folosete socket-uri se ncadreaz n modelul


client/server de concepere a unei aplicaii. n acest model aplicaia
este format din dou categorii distincte de programe numite
servere, respectiv clieni. Programele de tip server sunt cele care
ofer diverse servicii eventualilor clieni, fiind n stare de ateptare
atta vreme, ct nici un client nu le solicit serviciile Programele de
tip client sunt cele care iniiaz conversaia cu un server, solicitnd
un anumit serviciu. Uzual, un server trebuie s fie capabil s trateze
mai muli clieni simultan i, din acest motiv, fiecare cerere
adresat serverului va fi tratat ntr-un fir de execuie separat.
4.3 Comunicarea prin conexiuni
n acest model se stabilete o conexiune TCP ntre un program
client i un server, care furnizeaz un anumit serviciu.
4.3.1 Structura general a unui server bazat pe conexiuni
while (true) {
accept a connection ;
create a thread to deal with the client ;
}//
end while

Exemplu de program server:


import java.net.*;
import java.io.*;
public class SimpleServer extends Thread {
// Definesc portul pe care se gsete serverul
n afara intervalului 1-1024:

65

public static final int PORT = 8100;


private static ServerSocket serverSocket =
null;
private Socket clientSocket = null;
public void run() {
//Executa solicitarea clientului
String cerere, raspuns;
try {
//in este fluxul de intrare de la client
BufferedReader n = new
BufferedReader(new InputStreamReader(
clientSocket.getInputStream() ));
//out este flux de ieire ctre client
PrintWriter out = new PrintWriter(
clientSocket.getOutputStream() );
//primesc cerere de la client
cerere = in.readLine();
//trimit raspuns clientului
raspuns = "hello " + cerere;
out.println(raspuns);
out.flush();
} catch (IOException e) {
System.err.println("Eroare de
citire/scriere \n" + e);
} finally {
// nchid socketul deschis pentru clientul curent
try {
clientSocket.close();
} catch (IOException e) {
System.err.println("Socketul nu poate fi
inchis \n" + e);
}
}
}
public SimpleServer() throws IOException {
serverSocket = new ServerSocket(PORT);

66

try {
//Asteapta un client
clientSocket = serverSocket.accept();
//Execut solicitarea clientului ntr-un fir de
execuie
new Thread(this).start();
} finally {
serverSocket.close();
}
}
public static void main(String[] args)
throws IOException {
SimpleServer server = new SimpleServer();
}
}

4.3.2 Structura general a unui client bazat pe conexiuni


import java.net.*;
import java.io.*;
public class SimpleClient {
public static void main(String[] args) throws
IOException {
//adresa IP a serverului
String serverAddress = "127.0.0.1";
//portul la care serverul ofera serviciul
int PORT = 8100;
Socket clientSocket = null;
PrintWriter out = null;
BufferedReader n = null;
String cerere, raspuns;
try {
clientSocket = new Socket(serverAddress, PORT);
out = new PrintWriter(
clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(

67

clientSocket.getInputStream()));
//se trimite o cerere la server
cerere = "duke";
out.println(cerere);
//se asteapta raspuns de la server
raspuns = in.readLine();
System.out.println(raspuns);
} catch (UnknownHostException e) {
System.err.println("Serverul nu poate
fi gasit \n" + e);
System.exit(1);
} finally {
if (out != null)
out.close();
if (in != null)
in.close();
if (clientSocket!= null)
clientSocket.close();
}
}
}

4.4 Comunicarea prin datagrame


n acest model clientul trimite un pachet cu cererea ctre server,
acesta primete pachetul i returneaz rspunsul tot prin
intermediul unui pachet. Un astfel de pachet se numeste datagram
i este reprezentat printr-un obiect din clasa DatagramPacket.
Primirea i trimiterea datagramelor se realizeaz tot prin
intermediul unui socket, acesta fiind modelat printr-un obiect al
clasei DatagramSocket.
4.4.1 Structura general a unui server bazat pe datagrame
import java.net.*;
import java.io.*;
public class DatagramServer {

68

public static final int PORT = 8200;


private DatagramSocket socket = null;
DatagramPacket cerere, raspuns = null;
public DatagramServer() throws IOException {
Socket = new DatagramSocket(PORT);
try
{
while (true) {
//Declara pachetul n care va fi recepionat cererea
byte[] buf = new byte[256];
cerere = new
DatagramPacket(buf, buf.length);
//Atept apariia unui pachet cu cererea
socket.receive(cerere);
//Afl adresa i portul de la care vine cererea
InetAddress adresa = cerere.getAddress();
int port = cerere.getPort();
//Construiete rspunsul
buf = ("Hello " + new
String(cerere.getData())).getBytes();
//Trimite un pachet cu rspunsul ctre client
raspuns = new DatagramPacket(buf, buf.length,
adresa, port);
socket.send(raspuns);
}
} finally {
socket.close();
}
}
public static void main(String[] args) throws
IOException {
new DatagramServer();
}
}

69

4.4.2 Structura general a unui client bazat pe datagrame


import java.net.*;
import java.io.*;
public class DatagramClient {
public static void main(String[] args) throws
IOException {
//adresa IP i portul la care ruleaz serverul
InetAddress address =
InetAddress.getByName("127.0.0.1");
int port=8200;
DatagramSocket socket = null;
DatagramPacket packet = null;
byte buf[];
try {
//Construiete un socket pentru comunicare
socket = new DatagramSocket();
//Construiete i trimite pachetul cu cerere ctre
server
buf = "Duke".getBytes();
packet = new DatagramPacket(buf,
buf.length, address, port);
socket.send(packet);
//Ateapt pachetul cu rspunsul de la server
buf = new byte[256];
packet = new DatagramPacket(buf,
buf.length);
socket.receive(packet);
//Afieaz rspunsul
System.out.println(new
String(packet.getData()));

} finally {
socket.close();
}

70

4.4.3 Trimiterea de mesaje ctre mai multi clieni


Diverse situaii impun gruparea mai multor clieni astfel, nct un
mesaj (pachet) trimis pe adresa grupului s fie recepionat de
fiecare dintre acetia. Gruparea mai multor programe n vederea
trimiterii multiple de mesaje se realizeaz prin intermediul unui
socket special, descris de clasa MulticastSocket, extensie a clasei
DatagramSocket. Un grup de clieni abonai pentru trimitere
multipl este specificat printr-o adres IP din intervalul 224.0.0.1
- 239.255.255.255 i un port UDP. Adresa 224.0.0.0 este
rezervat i nu trebuie folosit.
4.4.4 nregistrarea unui client ntr-un grup
import java.net.*;
import java.io.*;
public class MulticastClient {
public static void main(String[] args) throws
IOException {
//adresa IP i portul care reprezint grupul de
clieni
InetAddress group =
InetAddress.getByName("230.0.0.1");
int port=4444;
MulticastSocket socket = null;
byte buf[];
try {
//Se altura grupului aflat la adresa i portul
specificate
socket = new MulticastSocket(port);
socket.joinGroup(group);

71

//ateapt un pachet venit pe adresa grupului


buf = new byte[256];
DatagramPacket packet = new
DatagramPacket(buf, buf.length);
socket.receive(packet);
System.out.println(new
String(packet.getData()));
} finally {
socket.leaveGroup(group);
socket.close();
}
}

4.4.5 Transmiterea unui mesaj ctre un grup


import java.net.*;
import java.io.*;
public class MulticastSend {
public static void main(String[] args) throws
Exception {
InetAddress group =
InetAddress.getByName("230.0.0.1");
int port = 4444;
byte[] buf;
DatagramPacket packet = null;
//Creeaz un socket cu un numar oarecare
DatagramSocket socket = new DatagramSocket(0);
try{
//Trimite un pachet ctre toi clienii din grup
buf = (new String("Salut grup")).getBytes();
packet = new DatagramPacket(buf, buf.length,
group, port);
socket.send(packet);

72

} finally {
socket.close();
}

Lucrare de laborator nr. 4

1. Tema lucrrii:
Implementarea aplicaiilor client-server multi-thread
2. Scopul lucrrii:
nsuirea metodelor de utilizare a thread-urilor n Java pentru
aplicaiile client-server
3. Etapele de realizare:
1) Utilizarea clasei Thread pentru crearea unei clase noi;
2) Utilizarea interfeei Runnable pentru crearea i lansarea
de thread-uri;
3) Crearea aplicaiei client / server pentru un singur
utilizator;
4) Crearea aplicaiei client / server pentru un numr fix
de clieni, prin utilizarea thread-urilor;
5) Utilizarea interfeei grafice pentru appleturile multi
user.
4. Exemplu de realizare:
1. Trebuie s includ clasa client
2. Trebuie s includ clasa server
3. Trebuie s includ clasa client / server pentru aplicaia
multiuser bazat pe threaduri
73

Program 1. Aplicaie client-server:


Serverul trimie napoi textul trimis de care client.
import java.io.*;
import java.net.*;
public class client {
public static void main(String[] args) {

// declaration section:
// clientClient: our client socket
// os: output stream
// is: input stream
Socket clientSocket = null;
DataInputStream is = null;
PrintStream os = null;
DataInputStream inputLine =null;
// Initialization section:
// Try to open a socket on port 2222
// Try to open input and output streams
try {
clientSocket = new Socket("localhost", 2222);
os = new
PrintStream(clientSocket.getOutputStream());
is = new
DataInputStream(clientSocket.getInputStream());
inputLine = new DataInputStream(new
BufferedInputStream(System.in));
} catch (UnknownHostException e) {
System.err.println("Don't know about
host");
} catch (IOException e) {
System.err.println("Couldn't get I/O
for the connection to host");
}

74

// If everything has been initialized then we want


to write some data
// to the socket we have opened a connection to on
port 2222
if (clientSocket != null && os != null &&
is != null) {
try {
// keep on reading from/to the socket till we
receive the "Ok" from the server,
// once we received that then we want to break.
String responseLine;
os.println(inputLine.readLine());
while ((responseLine =
is.readLine()) != null) {
System.out.println(responseLine);
if
(responseLine.indexOf("Ok") != -1) {
break;
}
os.println(inputLine.readLine());
}
//
//
//
//

clean
close
close
close

up:
the output stream
the input stream
the socket

os.close();
is.close();
clientSocket.close();
} catch (UnknownHostException e) {
System.err.println("Trying to
connect to unknown host: " + e);
} catch (IOException e) {
System.err.println("IOException:
+ e);
}

75

"

import java.io.*;

import java.net.*;
public class server {
public static void main(String args[]) {
// declaration section:
// declare a server socket and a client socket for
the server
// declare an input and an output stream
ServerSocket echoServer = null;
String line;
DataInputStream is;
PrintStream os;
Socket clientSocket = null;
//
//
if
//

Try to open a server socket on port 2222


Note that we can't choose a port less than 1023
we are not
privileged users (root)
try {
echoServer = new ServerSocket(2222);
}
catch (IOException e) {
System.out.println(e);
}

// Create a socket object from the ServerSocket to


listen and accept
// connections.
// Open input and output streams
try {
clientSocket = echoServer.accept();

76

is = new
DataInputStream(clientSocket.getInputStream());
os = new
PrintStream(clientSocket.getOutputStream());
// As long as we receive data, echo that data back
to the client.
while (true) {
line = is.readLine();
os.println("From server: "+ line);
}
}
catch (IOException e) {
System.out.println(e);
}
}

Program 2. Aplicaie client-server multiuser:


Serverul deservete mai muli clieni ce particip la discuie.
//ClientMM.java
import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
class Interface extends Frame{
Dialog form = null;
TextArea wnd_msg = null,wnd_names=null;
Button b_send = null;
TextField to_send = null;
Interface(String window_name){
super(window_name);
setFont(new Font("Comic",Font.BOLD,12));
setBackground(Color.green);
wnd_msg = new
TextArea("",14,50,TextArea.SCROLLBARS_VERTICAL_ONLY
);
wnd_msg.setEditable(false);

77

wnd_names = new
TextArea("",14,10,TextArea.SCROLLBARS_VERTICAL_ONLY
);
wnd_names.setEditable(false);
to_send = new TextField(50);
b_send = new Button("Send");
Panel for_sends = new Panel();
Panel for_wnds = new Panel();
for_sends.add(to_send);
for_sends.add(b_send);
for_wnds.add(wnd_names);
for_wnds.add(wnd_msg);
add(for_sends,BorderLayout.SOUTH);
add(for_wnds,BorderLayout.CENTER);
setSize(500,340);
setVisible(true);
}
}
class Form extends Dialog implements
ActionListener{
public TextField for_host=null,for_name=null;
public Button begin = null;
public String[] host = null;
public String[] name = null;
Label l_host = new Label("Host:
");
Label l_name = new Label("Name:");
Form(Interface chat,String[] host,String[] name){
super(chat , "Regestration:",true);
setFont(new Font("Comic",Font.BOLD,12));
this.host = host;
this.name = name;
for_host = new TextField(10);
for_name = new TextField(10);
Panel for_texts = new Panel();
for_texts.add(l_host);
for_texts.add(for_host);
for_texts.add(l_name);
for_texts.add(for_name);
begin = new Button("Begin");
begin.addActionListener(this);
Panel for_button = new Panel();

78

for_button.add(begin);
add(for_texts,BorderLayout.CENTER);
add(for_button,BorderLayout.SOUTH);
setBackground(Color.green);
setSize(180,115);
setVisible(true);
}
public void actionPerformed(ActionEvent ae){
host[0] = for_host.getText();
name[0] = for_name.getText();
dispose();
}
}
class Read implements ActionListener{
// Transmiterea mesajului
Interface wnd;
String name="not :";
Socket ss;
PrintWriter out = null;
String fromUser="";
Read(Socket s,Interface w){
wnd=w;
ss=s;
try{
out = new PrintWriter(ss.getOutputStream(),
true);
}catch(IOException io){}
}
public void actionPerformed(ActionEvent ae){
fromUser=wnd.to_send.getText();
wnd.to_send.setText("");
out.println(name+fromUser);
fromUser="";
}
public void setName(String name){
this.name=name+" : ";
}
}
public class ClientMM{
public static void main(String[] args) throws
IOException {

79

String[] name = new String[1] , host = new


String[1];
Interface wnd = new Interface("Chat");
new Form(wnd,host,name);
Read reader = null;
Socket kkSocket = null;
BufferedReader in = null;
try {
kkSocket = new Socket(host[0], 8888);
in = new BufferedReader(new
InputStreamReader(kkSocket.getInputStream()));
} catch (UnknownHostException e) {
System.err.println("Don't know about
host.");
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O
for the connection.");
System.exit(1);
}
String fromServer;
reader=new Read(kkSocket,wnd);
reader.setName(name[0]);
wnd.setTitle(name[0]);
final PrintWriter out = reader.out;
// mesaje de sistem
out.println("^"+name[0]);
wnd.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent we){
out.println("2112333");
System.exit(0);
}
});
wnd.b_send.addActionListener(reader);
wnd.to_send.addActionListener(reader);
while(((fromServer = in.readLine()) !=
null) && (reader!=null)) {
if((fromServer.charAt(0)=='^') &&
(fromServer.charAt(1)=='#')){
wnd.wnd_names.setText("");
continue;

80

}
if(fromServer.charAt(0)=='^'){
wnd.wnd_names.append(fromServer.substring(1)+'\n');
}else
wnd.wnd_msg.append(fromServer+"\n");
}
in.close();
kkSocket.close();
}
}
//ServerMM.java
import java.net.*;
import java.io.*;
public class ServerMM {
public static void main(String[] args) throws
IOException {
Socket[] mysocks=new Socket[10];
String[] names = new String[10];
Socket temp_sock = null;
int count=0;
for(int i=0;i<10;i++){
mysocks[i] = null;
names[i] = null;
}
ServerSocket serverSocket = null;
boolean listening = true;
try {
serverSocket = new ServerSocket(8888);
} catch (IOException e) {
System.err.println("Could not listen on
port: 8888.");
System.exit(-1);
}
while (listening){
if(count<10
&&((temp_sock=serverSocket.accept())!=null)){
for(int i=0;i<10;i++){

81

if(mysocks[i]==null){
count=i;
mysocks[i]=temp_sock;
temp_sock=null;
new
ServerThread(mysocks[count],mysocks,count,names).st
art();
break;
}
}
count=0;
for(int i=0;i<10;i++)
if(mysocks[i]!=null) count++;
System.out.println(count);
}else System.out.println("Server is full");
}
}

serverSocket.close();
}

class ServerThread extends Thread{


String names[] = null;
String name = null;
public int own_num;
private Socket socket = null;
private Socket[] mys=null;
PrintWriter outAll = null;
public ServerThread(Socket socket,Socket[]
my,int num,String names[]) {
super("ServerThread");
this.socket = socket;
this.mys = my;
own_num = num;
this.names = names;
}
public void run() {
try {
PrintWriter out = new
PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(

82

new InputStreamReader(
socket.getInputStream()));
String inputLine, outputLine;
while ((inputLine = in.readLine())!= null) {
if (inputLine.equalsIgnoreCase("2112333"))
for(int i = 0;i<10;i++){
if(names[i] != null)

if(name.compareTo(names[i])==0){
names[i] = null;
break;
}
}
for(int i=0;i<10;i++){
if(mys[i]!=null){
outAll = new
PrintWriter(mys[i].getOutputStream(), true);
outAll.println("^#");
for(int j=0;j<10;j++){
if(names[j] != null)
outAll.println(names[j]);
}
outAll = null;
}
}
break;
}
if(inputLine.charAt(0)=='^'){
for(int i = 0;i < 10; i++){
if(names[i] == null){
name = inputLine;
names[i] = inputLine;
break;
}
}
for(int i=0;i<10;i++){
if(mys[i]!=null){
outAll = new
PrintWriter(mys[i].getOutputStream(), true);

83

outAll.println("^#");
for(int j=0;j<10;j++){
if(names[j] != null)
outAll.println(names[j]);
}
outAll = null;
}
}
}else
for(int i=0;i<10;i++){
if(mys[i]!=null){
outAll = new
PrintWriter(mys[i].getOutputStream(), true);
outAll.println(inputLine);
outAll = null;
}
}
}
socket.close();
mys[own_num]=null;
out.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

5. Probleme propuse:
1. Se cere elaborarea unei perechi de programe client-server cu
urmtoarea funcionalitate:
serverul creeaz la prima conectare a unui client un
fiier text vid cu numele acestuia (clientului);
clientul poate trimite serverului unul sau mai multe
fiiere i cere interclasarea acestora cu fiierul care-i
84

poart numele; rezultatul interclasrii va fi reinut n


fiierul cu numele clientului;
clientul poate cere serverului returnarea coninutului
fiierului ce-i poart numele.
OBS. Se cere scrierea unui server concurent folosind thread-uri.
Interclasarea fiierelor se va face folosind thread-uri.
2. Se cere elaborarea unei perechi de programe client-server cu
urmtoarea funcionalitate:
serverul calculeaz expresii aritmetice primite de la
clieni i pstreaz rezultatul fiecrei execuii n cte un
fiier text cu numele clientului.
clientul poate trimite dou sau mai multe expresii
aritmetice (n forma polonez postfixat/prefixat)
pentru a fi evaluate, poate cere rezultatele tuturor
execuiilor anterioare, sau al unei execuii anume.
OBS. Se cere scrierea unui server concurent folosind thread-uri.
Evaluarea expresiilor i scrierea rezultatului n fiierul text se va
face folosind thread-uri. Se cere sincronizarea accesului la fiierul
cu rezultate.
3. Se cere elaborarea unei perechi de programe client-server cu
urmtoarea funcionalitate:
serverul gestioneaz o list de fiiere.
clienii pot cere lista fiierelor disponibile, pot trimite
noi fiiere, respectiv pot cere returnarea coninutului
unui anumit fiier - ordonat alfabetic pe linii.
OBS. Se cere scrierea unui server concurent folosind thread-uri.
Accesul la lista de fiiere va fi sincronizat. Ordonarea fiierelor text
se va face folosind thread-uri (se cere ca fiierele text s aib
dimensiunea de minim 500 Kb).

85

4. Se cere elaborarea unei perechi de programe client-server cu


urmtoarea funcionalitate:
serverul gestioneaz o list de fiiere.
clienii pot cere lista fiierelor disponibile, pot trimite
noi fiiere, respectiv pot cere returnarea rezultatului
interclasrii a dou sau mai multe fiiere.
OBS. Se cere scrierea unui server concurent folosind thread-uri.
Accesul la lista de fiiere va fi sincronizat. Inteclasarea fiierelor
text se va face folosind thread-uri(se cere ca fiierele text s aib
dimensiunea de minim 500 Kb).
5. Se cere elaborarea unei perechi de programe client-server cu
urmtoarea funcionalitate:
serverul ntreine un fiier text cu triplete de forma
(produs-pre-timpul n care produsul e disponibil pentru
licitaie).
clienii pot cere lista produselor, respectiv pot licita
pentru un produs.
serverul face update-ul n fiierul de produse (la
expirarea timpului) i notific clienii interesai
de
rezultatul licitaiei.
OBS. Se cere scrierea unui server concurent folosind thread-uri.
Accesul la lista de produse va fi sincronizat.
6. Se cere elaborarea unei perechi de programe client-server cu
urmtoarea funcionalitate:
serverul simuleaz un ghieu de banc la care se pot
face extrageri de numerar, respectiv alimentri de cont.
clienii se autentific pe baza unui numr de cont i a
unei parole i pe lng cele dou operaii menionate
(extragere/alimentare) pot cere i un istoric al
tranzaciilor (listarea tuturor tranzaciilor, nsumarea
tuturor
depunerilor/extragerilor).
86

OBS. Se cere scrierea unui server concurent folosind thread-uri.


Accesul la lista de clieni i la istoricul tranzaciilor va fi
sincronizat. nsumarea depunerilor/extragerilor se va face folosind
thread-uri/procese.
7. Se cere elaborarea unei perechi de programe client-server cu
urmtoarea funcionalitate:
serverul reine ntr-o structur de directoare proprii
(pentru fiecare client un director) imagini n diferite
formate (gif/jpg/etc...);
clientul poate acorda drepturi rw pentru directorul
propriu i altor clieni.
clientul poate cere adugarea, tergerea unei/unor
imagini n/din directorul propriu (respectiv directoarele
pentru care are drepturi corespunzatoare).
OBS. Se cere scrierea unui server concurent folosind thread-uri.
Accesul la lista de drepturi i la directoarele clienilor va fi
sincronizat. Adugarea/tergerea imaginilor se va face folosind
thread-uri.
8. Se cere elaborarea unei perechi de programe client-server cu
urmtoarea funcionalitate:
serverul simuleaz un server de mail: stocheaz
mesajele pentru clieni, respectiv trimite mesaje pentru
ali clieni.
un client poate vizualiza/terge mesajele
primite/trimise i poate trimite mesaje ctre ali
utilizatori.
OBS. Se cere scrierea unui server concurent folosind thread-uri.
Vizualizarea/tergerea/trimiterea de mesaje se va face folosind
thread-urie.

87

9. Se cere elaborarea unei perechi de programe client-server cu


urmtoarea funcionalitate:
serverul simuleaz o bibliotec electronic n sensul
c pune la dispoziia clienilor cri (fiiere text).
clienii pot consulta lista de cri din bibliotec,
respectiv pot "mprumuta" o carte (serverul mut
fiierul la client) pentru o perioad pe care o specific.
Dup expirarea timpului de mprumut clientul
"returneaz" cartea bibliotecii (mut fiierul napoi la
server).
serverul verific dup fiecare transfer "integritatea"
crii (folosind sume de control, semnturi digitale,
etc.), iar n cazul n care o carte nu e disponibil pune
doritorul n ateptare pn la returnarea crii.
OBS. Se cere scrierea unui server concurent folosind thread-uri.
Accesul la lista crilor va fi sincronizat.
mprumuturile/returnarea/verificarea integritii se va face
folosind thread-uri.
10. Se cere elaborarea unei perechi de programe client-server cu
urmtoarea funcionalitate:
serverul simuleaz un operator telefonic (reine
pentru fiecare client un nume i un numr de telefon);
la intervale aleatoare clienii "vorbesc" la telefon
(trimit spre server o pereche de numere reprezentnd
numrul de impulsuri asociat convorbirii i numrul cu
care s-a vorbit);
la sfrit de lun serverul trimite fiecrui client nota
de plat (nr. de impulsuri*valoare impuls), i poate
furniza la cerere un istoric al convorbirilor pentru
ultimele x luni.
OBS. Se cere scrierea unui server concurent folosind thread-uri.
Accesul la lista de clieni i la istoricul convorbirilor va fi
88

sincronizat. Calculul notei de plat pentru fiecare client se va face


folosind thread-uri.
BIBLIOGRAFIE
1. Doug Lea, Concurrent Programming n Java - Design
principles and PatternsAddison-Wesley 1996.
2. BruceEckel, free e-book Thinking n Java , Thinking n
Enterprise Java , www.BruceEckel.com
3. Cay S. Horstman, Garz Cornell, Core JavaTM 2v. 1, v. 2., Sun
Microsystems Press, 2002.

89

S-ar putea să vă placă și