Sunteți pe pagina 1din 12

Comunicarea prin canale

Modelul clasic
Ne ocupm n continuare de situaia cnd excludem o memorie comun ca posibilitate de
schimb de informaii ntre procese. Varianta luat n considerare n continuare este ca acest schimb
de informaii s se fac prin transmitere de informaii (mesaje) de la un proces la altul.
Exist o mare varietate de posibiliti de transmitere a mesajelor ntre procese (fire n Java).
Se disting trei grade principale de sincronizare:
1) Lipsa sincronizrii (transmiterea asincron). Sursa emite mesaje fr s se intereseze
dac precedentele mesaje au ajuns sau nu la destinaie. Inconvenientul evident este acela c atunci
cnd la destinaie ajunge un mesaj, destinatarul nu tie dac este sau nu vorba de ultima informaie
plecat de la surs. Similar, atunci cnd transmite un mesaj, sursa nu cunoate cte din precedentele
mesaje au ajuns la destinaie, deci nu cunoate gradul de informare al destinatarului. Transmiterea
asincron poate fi comparat cu corespondena prin pot.
2) Sincronizarea simpl. Dup ce emitorul a transmis un mesaj, el nu va mai trimite altul
(va atepta) pn cnd mesajul va fi recepionat de destinatar. Aceasta presupune c sursa este
informat dac destinatarul a preluat mesajul. Spunem c are loc o ntlnire (un rendez-vous),
imaginndu-ne c emitorul se ntlnete cu destinatarul pentru a-i nmna mesajul. Transmiterea
unor informaii prin fax poate fi ncadrat n acest mod de sincronizare: emitorul primete imediat
o confirmare din partea destinatarului c a primit mesajul.
3) Invocarea la distan (rendez-vous extins). n plus fa de sincronizarea simpl,
emitorul primete nu numai confirmarea de recepie, dar i un rspuns din partea destinatarului;
abia dup aceea emitorul poate transmite un alt mesaj.
n continuare ne vom rezuma la sincronizarea simpl.
Sincronizarea simpl are la baz lucrul cu canale. Afirmm c:
Utilizarea canalelor reprezint modalitatea cea mai natural
pentru rezolvarea problemei sincronizrii.
n prezentarea clasic, un canal are o singur surs i o singur destinaie, operaiile fiind
urmtoarele:
- c ! e cu semnificaia c expresia e este transmis canalului c;
- c ? v cu semnificaia c variabila v primete valoarea asociat canalului c.
Pentru fiecare (nume de) canal, procesul surs i procesul destinaie sunt unic
determinate: sursa este unicul proces ce conine operaia c ! e, iar destinaia este unicul proces n
care apare operaia complementar c ? v.
S observm ns c procesul surs nu cunoate identitatea procesului destinaie i nici
procesul destinaie nu tie de la ce proces primete mesajul. Legtura este considerat
unidirecional. Un canal transmite date, dar nu poate memora date.
Cele dou operaii sunt sincronizate, n sensul c transmisia are loc efectiv numai dac
procesul emitor este gata s transmit, iar procesul de destinaie este gata s recepioneze; n caz
contrar, procesul care ajunge primul la "ntlnire" (rendez-vous) este blocat n ateptarea celuilalt.
Nici un proces nu poate avansa nainte de terminarea transmiterii.
Precizm c expresia transmis prin canal i variabila care o recepioneaz trebuie s aib
acelai tip.
1

Faciliti Java pentru lucrul cu canale


Pachetul java.util.concurrent pune la dispoziie clasa:
public class SynchronousQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, Serializable

Cozile sincrone sunt similare canalelor de tip rendez-vous din CSP i Ada, cu excepia
faptului c mai multe fire pot ncerca s pun n, respectiv s ia din, coad. Orice operaie de
inserare trebuie s atepte operaia corespunztoare de extragere efectuat de alt fir i viceversa.
O coada sincron nu are capacitate, ceea ce exclude multe operaii asupra cozilor. Nu este
permis inserarea n coad a lui null.
Clasa permite o politic de fairness, stabilit prin constructor.
Constructorii clasei sunt urmtorii:
public SynchronousQueue()

creaz o coad sincron fr politic de fairness.


public SynchronousQueue(boolean fair)

creaz o coad sincron pentru care vom alege o politic de fairness punnd true ca
argument; altfel, ordinea de deblocare a firelor nu corespunde neaprat ordinii de blocare.
Pentru ncercrile de a pune/lua cu ateptarea operaiei
complementare sunt disponibile metodele put i take.
Pentru ncercri de a pune/lua imediat sunt disponibile metodele
offer i poll.

Metode care conduc la blocare pn cnd cerinele lor pot fi satisfcute :

void put(E e) throws InterruptedException, ClassCastException,


NullPointerException, IllegalArgumentException
introduce elementul e n coad, ateptnd - dac este necesar - ca alt fir s-l primeasc.
E take() throws InterruptedException

detecteaz i ntoarce (prin tergere) capul cozii, ateptnd - dac este necesar - ca alt fir s-l
insereze.

Metode care ntorc o valoare atunci cnd nu pot fi satisfcute imediat :

boolean offer(E e) throws ClassCastException,


NullPointerException, IllegalArgumentException
adaug n coad, dac este imediat posibil, elementul e. ntoarce true dac adugarea a

avut loc.
E poll()

ntoarce capul cozii, tergndu-l din coad; dac la momentul invocrii coada este vid,
ntoarce null. Este folosit atunci cnd ncercm s citim din unul sau mai multe canale,
cu alternativ (eventual vid) n caz de eec.

Conducte de canale
Exemplul 1. Generarea primelor n numere prime folosind canale.
F0
2

c0

...

ci-1

Fi

ci

...

cn

Fn+1

Vom lucra cu urmtoarele fire de executare (de tipul Filtru) i canale (de tipul
SynchronousQueue<Integer>):
- firul F0 pompeaz prin canalul c0 numerele naturale 2,3,... ctre firul F1;
- fiecare dintre firele Fi (i=1,...,n) primete valori prin canalul ci-1 (notat n program prin
st) i trimite valori lui Fi+1 prin canalul ci (notat n program prin dr);
- firul Fn+1 primete o valoare prin canalul cn, tiprete mesajul "STOP" i se termin.
Fiecare dintre firele Fi (i=1,...,n) acioneaz astfel:
- memoreaz prima valoare primit, numit valoarea sa de baz;
- "filtreaz" urmtoarele numere pe care le primete, n sensul c transmite la dreapta pe
conduct numrul primit din stnga doar dac acesta nu este divizibil cu valoarea sa de baz.
Valoarea de baz a fiecrui fir Fi (i=1,...,n) este un numr prim i anume al i-lea numr
prim, deoarece este cel mai mic numr natural nedivizibil cu vreun numr prim mai mic dect el.
Pentru terminarea programului, declarm firele Fi (i=0,...,n) ca fiind fire daemon,
astfel nct terminarea firului principal i a firului Fn+1 determin terminarea ntregului program.
import java.util.*; import java.util.concurrent.*;
class Filtru extends Thread {
static int n;
int id; SynchronousQueue<Integer> st,dr;
Filtru(int i, SynchronousQueue<Integer> s,
SynchronousQueue<Integer> d) {
id = i; st = s; dr = d;
}
public void run() {
int k=2; int x,y;
try {
if (id==0) while(true) dr.put(k++);
else if(id==n+1) {
st.take(); System.out.println("STOP");
}
else {
x = st.take(); System.out.print(x + "\t");
while(true) {
y = st.take(); if(y%x != 0) dr.put(y);
}
}
}
catch(InterruptedException e) { }
}
}
class Prime {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("n = "); int n = sc.nextInt();
Filtru.n = n; Filtru[] F = new Filtru[n+2];
SynchronousQueue<Integer> st=null,
dr = new SynchronousQueue<Integer>();
for(int i=0; i<n+2; i++) {
F[i] = new Filtru(i,st,dr);
st = dr; dr = new SynchronousQueue<Integer>();
3

}
for(int i=0; i<n+1; i++) F[i].setDaemon(true);
for(int i=0; i<n+2; i++) F[i].start();

Exemplul 2. Calcul de sume folosind conducte.


ntr-un fiier apare o secven de numere ntregi. Se cere s calculm suma lor folosind
un numr de n fire care dorim s fie "tot timpul" active. Pentru simplificare, se consider c
lungimea secvenei este multiplu de n.
Vom calcula sume de cte n numere consecutive din secven, folosind o conduct de
canale.
sus
F0

sus

sus
dr

...

st

Fi

dr

...

st

Fn-1

Filtrul F0 primete succesiv primul termen al sumelor prin canalul sus.


Fiecare fir/filtru Fi (0<i<n) primete succesiv prin canalul sus al i-lea termen al
sumelor, iar prin canalul st primete suma termenilor precedeni; dup nsumarea acestor dou
valori, trimite rezultatul lui Fi+1 prin canalul dr.
Filtru Fn-1 primete succesiv prin canalul sus ultimul termen al sumelor, iar prin canalul
st primete suma termenilor precedeni; dup nsumarea acestor dou valori, afieaz rezultatul
i incrementeaz cmpul static suma al clasei Filtru de tipul creia sunt firele.
Valoarea n este citit de la intrarea standard, iar grupele de n termeni sunt citite dintr-un
fiier a.b.
Oprirea programului este realizat prin declararea firelor Fi ca fire daemon.
import java.util.concurrent.*;
import java.util.*; import java.io.*;
class Filtru extends Thread {
static int n,suma=0; int x;
int id; SynchronousQueue<Integer> sus,st,dr;
Filtru(int i, SynchronousQueue<Integer> sus,
SynchronousQueue<Integer> s,
SynchronousQueue<Integer> d) {
id = i; this.sus = sus; st = s; dr = d;
}
public void run() {
while(true) {
try {
if (id==0) dr.put(sus.take());
else if(id==n-1) {
x = sus.take() + st.take(); suma += x;
System.out.print(x + "\t");
}
else dr.put(sus.take()+st.take());
}
catch(InterruptedException e) { }
}
}
}
4

class SumeCanale {
public static void main(String[] args) throws Exception {
System.out.print("n = ");
int n = new Scanner(System.in).nextInt();
Scanner in = new Scanner(new FileInputStream("a.b"));
Filtru.n = n;
Filtru[] F = new Filtru[n];
ArrayList<SynchronousQueue<Integer>> sus =
new ArrayList<SynchronousQueue<Integer>>();
for(int i=0; i<n; i++)
sus.add(new SynchronousQueue<Integer>());
SynchronousQueue<Integer> st=null,
dr = new SynchronousQueue<Integer>();
for(int i=0; i<n; i++) {
F[i] = new Filtru(i,sus.get(i),st,dr);
st = dr; dr = new SynchronousQueue<Integer>();
}
for(int i=0; i<n; i++) F[i].setDaemon(true);
for(int i=0; i<n; i++) F[i].start();
while(in.hasNextInt())
for(int i=0; i<n; i++)
sus.get(i).put(in.nextInt());
Thread.sleep(10); // se astepta afisarea ultimei sume partiale
System.out.println("\nSuma = " + Filtru.suma);
}

Observaie. Diagrama ocuprii firelor are forma unui paralelogram cu laturile paralele
orizontale, "nclinat" spre dreapta (liniile paralele orizontale corespund firelor):
Fir

Un mod tipic de lucru cu canale


Un mod tipic de lucru (folosit n cele ce urmeaz) este ca firele "efective" s transmit
comenzi prin intermediul aceluiai canal unui fir supervizor, care n mod repetat: primete
comenzi, identific firul "emitor" i execut comanda respectiv.
n plus, de obicei supervizorul este singurul care deine controlul asupra resurselor
comune, excluderea reciproc fiind astfel automat realizat.
Fir[i]
canal
Control
5

Exemplul 3. Problema filozofilor chinezi.


Fiecruia dintre cei n filozofi i este asociat un proces F[i]. Aceste procese, mpreun cu
procesul Control ce asigur o desfurare corect a operaiilor, sunt executate concurent.
F[i]
canal
Control

Fiecare fir F[i] trimite n ordine prin canal mai nti perechea (i,'M') i apoi perechea
(i,'G') pentru a iniia cele dou operaii (de a mnca i de a gndi). Cele dou operaii sunt
validate de firul Control.
Evidena beioarelor disponibile pentru fiecare filozof este asigurat de tabloul b; fiecare
dintre componentele b[i] poate lua numai una dintre valorile 0,1,2, reprezentnd numrul de
beioare pe care le poate ridica filozoful F[i].
Nu este necesar o excludere reciproc asupra beioarelor, deoarece ele sunt folosite numai
n firul Control.
Dac firul Control primete, prin canalul canal, o "comand" (i,'M'), i va da curs
doar dac b[i]=2 :
- n caz afirmativ, va actualiza numrul de beioare disponibile pentru filozoful din stnga i
pentru filozoful din dreapta sa i va marca (prin valoarea boolean mananca[i]) faptul c acum
filozoful F[i] mnnc.
- n caz contrar, ncercarea de a mnca este pierdut; cum ea este urmat de comanda
(i,'G'), trebuie ca aceasta s nu determine vreo aciune.
Dac firul Control primete o "comand" (i,'G'), i va da curs numai dac
mananca[i] are valoarea true. n caz afirmativ, va actualiza numrul de beioare disponibile
pentru filozoful din stnga i pentru filozoful din dreapta sa i va marca (prin valoarea boolean
mananca[i]) faptul c acum filozoful F[i] nu mai mnnc.
Vom declara firul control i firele F[i] ca fire daemon. Drept urmare, terminarea firului
principal (realizat prin citirea unui ir de caractere) va antrena terminarea ntregului program.
Am ales ca firele corespunztoare filozofilor s fie create n constructorul clasei Control.
import java.util.*;
import java.util.concurrent.*;
class Fil_Canale {
public static void main(String[] sss) throws Exception {
Control control = new Control();
control.setDaemon(true); control.start();
System.in.read();
}
}
class C { // Cerere
int i; char c;
C(int ii, char cc) { i = ii; c = cc; }
}
6

class Filozof extends Thread {


int id; SynchronousQueue<C> canal;
Filozof(int i, SynchronousQueue<C> c) { id = i; canal = c; }
static void delay(int i) {
try { Thread.sleep( (int) (i*Math.random()) ); }
catch(InterruptedException e) { }
}
public void run() {
try {
for(int i=0; i<10; i++) {
canal.put(new C(id,'M')); delay(100);
canal.put(new C(id,'G')); delay(100);
}
}
catch(InterruptedException e) { }
}
}
class Control extends Thread {
int n,i;
SynchronousQueue<C> canal = new SynchronousQueue<C>();
int[] b; boolean[] mananca;
Filozof[] F;
Scanner sc = new Scanner(System.in);
Control() {
System.out.print("n = "); n = sc.nextInt();
b = new int[n]; mananca = new boolean[n];
for(int i=0; i<n; i++) {
mananca[i] = false; b[i] = 2;
}
F = new Filozof[n];
for(i=0; i<n; i++) F[i] = new Filozof(i,canal);
for(i=0; i<n; i++) F[i].setDaemon(true);
for(i=0; i<n; i++) F[i].start();
}
public void run() {
int st,dr,i;
try {
while(true) {
C Ob = canal.take();
st = (Ob.i==0 ? n-1 : Ob.i-1); dr = (Ob.i+1) % n;
if(Ob.c == 'M')
if( (b[Ob.i] == 2) ) {
b[st]--; b[dr]--;
mananca[Ob.i] = true;
System.out.print(Ob.i + "M" + "\t");
}
else System.out.print("~" + Ob.i + "M" + "\t"); //ratare
else if(mananca[Ob.i]) {
b[st]++; b[dr]++;
System.out.print(Ob.i + "G" + "\t");
mananca[Ob.i] = false;
}
7

}
}
catch(InterruptedException e) { }

Exemplul 4. Problema Cititori - Scriitori.


Vom folosi aceeai abordare ca n problema filozofilor. Celor nc cititori i celor ns scriitori
le este asociat cte un fir. Aceste fire sunt executate concurent cu firul Control care gestioneaz
toate operaiile asupra crii. Toi cititorii i scriitorii trimit prin unicul canal canal firului
Control perechi de cereri pentru deschiderea i nchiderea crii.
S[i]

C[i]
canal
Control

Firul Control primete pe rnd prin canal cte o cerere i, dac este posibil rezolvarea ei,
o duce la ndeplinire. n particular vor exista cereri de deschidere irosite/euate/pierdute, fapt de
care trebuie inut cont n primul rnd la cererea de nchidere corespunztoare.
Aciunile cititorilor, scriitorilor i procesului de control se execut concurent. Fiecare
aciune a unui cititor sau scriitor const n a transmite intenia sa de a deschide cartea i apoi de a o
nchide.
Fiecare cerere este o pereche (i,s) unde i este numrul de ordine la cititorului sau
scriitorului, iar s este unul dintre urmtoarele iruri de caractere:
- "cdesch" pentru deschidere pentru citire;
- "cinch" pentru nchidere pentru citire;
- "sdesch" pentru deschidere pentru scriere;
- "sinch" pentru nchidere pentru citire.
Procesul Control mai ine evidena cererilor de deschidere/nchidere prin intermediul
tablourilor citeste i scrie de dimensiuni nc respectiv ns, unde de exemplu citeste[i] este
true dac i numai dac cititorul i a deschis cu succes cartea, dar nu a nchis-o nc. Aceasta este
necesar deoarece cererile de deschidere care nu pot fi satisfcute sunt considerate euate (nu sunt
rencercate) i deci o cerere de nchidere trebuie validat doar dac cititorul/scriitorul a deschis cu
succes cartea.
Variabila nrcit ine evidena numrului de cititori activi, iar variabila boolean sactiva
va avea valoarea true dac i numai dac un scriitor are acces la carte.
import java.util.*; import java.util.concurrent.*;
class B {
static SynchronousQueue<C> canal = new SynchronousQueue<C>(true);
static void delay(int i) {
try { Thread.sleep( (int) (i*Math.random()) ); }
catch(InterruptedException e) { }
}
}
class C {
// Cerere
int i; String s;
C(int ii, String ss) { i = ii; s = ss; }
8

}
class Cititor extends Thread {
int id;
Cititor(int i) { id = i; }

public void run() {


try {
for(int i=0; i<10; i++) {
B.canal.put(new C(id,"cdesch")); B.delay(50);
B.canal.put(new C(id,"cinch")); B.delay(50);
}
}
catch(InterruptedException e) { }
}

class Scriitor extends Thread {


int id;
Scriitor(int i) { id = i; }

public void run() {


try {
for(int i=0; i<10; i++) {
B.canal.put(new C(id,"sdesch")); B.delay(10);
B.canal.put(new C(id,"sinch")); B.delay(10);
}
}
catch(InterruptedException e) { }
}

class Control extends Thread {


boolean sactiva; int nrcit, nc, ns;
boolean[] citeste, scrie;
Control(int c, int s) {
sactiva = false; nrcit = 0;
nc = c; ns = s;
citeste = new boolean[nc]; scrie = new boolean[ns];
for(int i=0; i<nc; i++) citeste[i] = false;
for(int i=0; i<ns; i++) scrie[i] = false;
}
public void run() {
try {
while(true) {
C Ob = B.canal.take();
if(Ob.s.equals("cdesch"))
if( !sactiva ) {
System.out.print("C" + Ob.i + "(\t");
nrcit++; citeste[Ob.i] = true;
}
else System.out.print("~C" + Ob.i + "(\t");
else if(Ob.s.equals("cinch")) {
if( citeste[Ob.i] ) {
System.out.print("C" + Ob.i + ")\t");
nrcit--; citeste[Ob.i] = false;
}
9

10

else;
}
else if(Ob.s.equals("sdesch")) {
if( !sactiva && (nrcit==0) ) {
System.out.print("S" + Ob.i + "(\t");
sactiva = true; scrie[Ob.i] = true;
}
else System.out.print("~S" + Ob.i + "(\t");
}
else if(Ob.s.equals("sinch") && scrie[Ob.i] ) {
System.out.print("S" + Ob.i + ")\t");
sactiva = false; scrie[Ob.i] = false;
}
else;

}
}
catch(InterruptedException e) { }

}
class CitScr_Canale {
public static void main(String[] sss) throws Exception {
int nc = 8, ns = 5, i;
Cititor[] C = new Cititor[nc];
for(i=0; i<nc; i++) C[i] = new Cititor(i);
Scriitor[] S = new Scriitor[ns];
for(i=0; i<ns; i++) S[i] = new Scriitor(i);
Control control = new Control(nc,ns);
for(i=0; i<nc; i++) C[i].start();
for(i=0; i<ns; i++) S[i].start();
control.setDaemon(true); control.start();
}
}

Situaiile de nedeterminare sunt urmtoarele:


- nicio persoan nu are acces la carte: orice cerere de deschidere pentru citire sau pentru scriere
poate fi satisfcut;
- cel puin un cititor este activ: oricare dintre cititorii activi poate nceta consultarea crii i
oricare alt cititor poate ncepe consultarea crii.

Clasa Exchanger
Tot n pachetul java.util.concurrent apare clasa:
public class Exchanger<tip> extends Object
Un obiect de tipul Exchanger reprezint un punct de sincronizare la care dou fire se pot
racorda pentru a schimba ntre ele elemente de tipul tip. Fiecare fir ofer un obiect ca argument
la invocarea metodei exchange; cnd ambele fire ajung la "ntlnire", are loc interschimbarea
obiectelor oferite.
Un astfel de obiect poate fi privit ca o form bidirecional a lui SynchronousQueue.
Aplicaiile se regsesc n algoritmii genetici i n proiectarea conductelor (canalelor).
Observaie (privind consistena memoriei). Pentru fiecare pereche de fire ce schimb
ntre ele cu succes obiecte, aciunile premergtoare lui exchange() au loc naintea celor ce
urmeaz unui return din invocarea exchange() corespunztoare din cellalt fir.
10

Constructorul are forma:


Exchanger<tip>()

Metoda:
public tip exchange(tip x) throws InterruptedException

ateapt un alt fir s ajung la punctul de schimb (afar de cazul cnd firul curent este n starea
"ntrerupt") i apoi are loc transferul bidirecional. Dac un alt fir ateapt deja la punctul de
schimb, executarea sa este reluat i are loc schimbul. Dac niciun fir nu ateapt, firul curent
devine "adormit" pn cnd fie alt fir ajunge la punctul de schimb, fie alt fir ntrerupe firul curent.
n cadrul schimbului, valoarea trimis este x, iar valoarea primit este cea returnat de
metod.
Dac firul curent este n starea "ntrerupt" cnd s-a ajuns la exchange() sau este
ntrerupt n timp ce ateapt schimbul, este lansat excepia InterruptedException i firul
iese din starea "ntrerupt".
Menionm c metoda are i o variant ce prevede o ateptare limitat n timp.
Exemplul 5. Revenim la problema celor dou tablouri s i t care n final trebuie s
conin cele mai mici, respectiv cele mai mari valori din totalitatea elementelor lor.
Soluia anterioar, cea care folosea semafoare, presupunea c ambele procese au acces la
ambele tablouri. n soluia care urmeaz, care folosete clasa Exchange, fiecare fir cunoate doar
propriul tablou.
Cele dou fire se ateapt pn cnd i calculeaz maximul, respectiv minimul
elementelor din tabloul propriu, dup care i interschimb cele dou valori. Firul corespunztor
lui s se termin cnd primete o valoare mai mic dect maximul elementelor proprii; firul
corespunztor lui t se termin cnd primete o valoare mai mare dect minimul elementelor
proprii.
import java.util.*; import java.util.concurrent.*;
class MinMaxEx {
public static void main(String[] www) throws Exception {
Exchanger schimb = new Exchanger<Integer>();
S FirS = new S(schimb); T FirT = new T(schimb);
FirS.start(); FirT.start();
FirS.join(); FirT.join();
FirS.scrie(); System.out.println(); FirT.scrie();
}
}
class S extends Thread {
int[] s; int ns; Exchanger schimb;
S(Exchanger e) {
schimb = e;
Scanner sc = new Scanner(System.in);
System.out.print("ns = "); ns = sc.nextInt();
s = new int[ns];
System.out.print("Dati cele ns=" + ns +" elemente: ");
for(int i=0; i<ns; i++) s[i] = sc.nextInt();
}
public void run() {
try {
Integer max = 0, min_t; int poz = -1;
while(true) {
11

12

max = Integer.MIN_VALUE;
for(int i=0; i<ns; i++)
if(s[i]>max) { max = s[i]; poz = i; }
min_t = (Integer) schimb.exchange(max);
System.out.println("S : am primit " + min_t);
if(min_t>max) break;
else s[poz] = min_t;

}
}
catch(Exception e) { }
}

void scrie() {
for(int i=0; i<ns; i++) System.out.print("\t" + s[i]);
}
}
class T extends Thread {
int[] t; int nt; Exchanger schimb;
T(Exchanger e) {
schimb = e;
Scanner sc = new Scanner(System.in);
System.out.print("nt = "); nt = sc.nextInt();
t = new int[nt];
System.out.print("Dati cele nt=" + nt +" elemente: ");
for(int i=0; i<nt; i++) t[i] = sc.nextInt();
}
public void run() {
try {
Integer min = 0, max_s; int poz = -1;
while(true) {
min = Integer.MAX_VALUE;
for(int i=0; i<nt; i++)
if(t[i]<min) { min = t[i]; poz = i; }
max_s = (Integer) schimb.exchange(min);
System.out.println("T : am primit " + max_s);
if(max_s<min) break;
else t[poz] = max_s;
}
}
catch(Exception e) { }
}
void scrie() {
for(int i=0; i<nt; i++) System.out.print("\t" + t[i]);
}
}

12

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