Vous êtes sur la page 1sur 12

Concuren I

1. Crearea unui task prin implementarea din clasa Runnable. Clasa LiftOff implementeaza Runnable.
In main, se creaza un obiect de tipul LiftOff task-ul se va executa intr-un fir de executie.
package concurenta1;
class LiftOff implements Runnable {
//fiecare obiect din clasa LiftOff
//va fi un task ce poate fi rulat pe un fir de executie
protected int countDown = 10; // Default
private static int taskCount = 0; //cont task-uri
private final int id = taskCount++;//contor nr obiecte din LiftOff
public LiftOff() {
}
public LiftOff(int countDown) {
this.countDown = countDown;
}
public String status() { //status-ul task-ului
return "#" + id + "(" + (countDown > 0 ? countDown : "Liftoff!") + "), ";
//constructia ? <=> id (countDown>0) concateneaza LiftOff
}
//metoda run() - implementata in interfata Runnable
public void run() { //lanseaza executia task-ului
while (countDown-- > 0) {
System.out.print(status());
Thread.yield(); //mesaj catre scheduler-ul de task-uri
//ca mare parte din ciclu s-a executat
//si se poate ocupa de alte task-uri intre timp
//este optional
}
}
}
package concurenta1;
public class MainThread {
public static void main(String[] args) {
LiftOff launch = new LiftOff();
launch.run(); //nu se creeaza fir nou de executie ci se ruleaza task-ul in firul prinicipal
}
}

Pentru a rula task-ul LiftOff intr-un fir de executie parallel utilizarea clasei Thread pentru a crea un
fir de execuie:
package concurenta1;
public class BasicThreads {
public static void main(String[] args) {
//task-ul este executat intr-un nou Thread
//clasa Thread asteapta in constructor un obiect
//ce implementeaza interfata Runnable
Thread t = new Thread(new LiftOff());
t.start();//se lanseaza executia in nou fir de executie: LiftOff.run()
System.out.println("Waiting for LiftOff");
}
}

Atentie la momentul in care este afisat mesajul din main (inainte sau dupa executia run)
package concurenta1;
public class MoreThreads {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) { //deschiderea a 5 fire
new Thread(new LiftOff()).start();
}
System.out.println("Waiting for LiftOff");
}
}

2. Crearea si managementul task-urilor prin Executors


package concurenta1;
import java.util.concurrent.*; //package-ul pentru concurenta
public class CachedThreadPool {
public static void main(String[] args) {
//creare obiectului ce va executa task-ul
//si va realiza managementul firelor de executie
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
exec.execute(new LiftOff()); //executarea task-ului
}
System.out.println("Waiting for executor to terminate!");
exec.shutdown(); //terminarea executiei obiectul exec nu va mai accepta task-uri noi
}
}

Atentie la metodele obiectelor Executors, de creare a diferitelor tipuri de Executori.


package concurenta1;
import java.util.concurrent.*;
public class FixedThreadPool {
public static void main(String[] args) {
// Constructor argument is number of threads:
//crearea unui thred pool cu numar fix de fire de executie
ExecutorService exec = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
exec.execute(new LiftOff());
}
System.out.println("Waiting for executor to terminate!");
exec.shutdown();
}
}

package concurenta1;
import java.util.concurrent.*;
public class SingleThreadExecutor {
public static void main(String[] args) {
ExecutorService exec = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
exec.execute(new LiftOff());
}
System.out.println("Waiting for executor to terminate!");
exec.shutdown();
}
}

3.Returnarea de valori din taskuri. Interfata Callable


Un Task creat prin implementarea Runnable poate executa procese paralele dar nu va returna
valori din interiorul unui fir de executie. Pentru returnarea valorilor din task-uri se
implementeaza interfata Callable.
package concurenta1;
import java.util.concurrent.*;
import java.util.*;
//interfata Callable utilizeaza generics pt specificarea
//tipului returnat din task
class TaskWithResult implements Callable<String> { //se returneaza String
private int id;

public TaskWithResult(int id) {


this.id = id;
}
//implementarea metodei call di interfata Callable
//inlocuieste metoda run() de la Runnable
public String call() {
return "result of TaskWithResult " + id;
}
}
public class CallableDemo {
public static void main(String[] args) {
//!!! un task Callable trebuie executat prin intermediul
//unui obiect Executor
ExecutorService exec = Executors.newCachedThreadPool();
ArrayList<Future<String>> results = new ArrayList<Future<String>>();
//colectia tine obiecte "VIITOARE" de tip String returnate de firele paralele
for (int i = 0; i < 10; i++) {
//submit realizeaza executia task-ului
//returneaza un obiect Future<String>
results.add(exec.submit(new TaskWithResult(i)));
}
System.out.println("Waiting for results: ");
for (Future<String> fs : results) {
try {
// get() blocks until completion:
//get() asteapta returnarea rezultatului din task
System.out.println(fs.get());
} catch (InterruptedException e) {
System.out.println(e);
return;
} catch (ExecutionException e) {
System.out.println(e);
} finally {
//inchiderea Executorului indiferent de rezultatul executiei
exec.shutdown();
}
}
}
}

4. Accesul concurent la resurse


package concurenta1;
public abstract class IntGenerator {
private volatile boolean canceled = false;
public abstract int next();
// Allow this to be canceled:
public void cancel() {
canceled = true;
}
public boolean isCanceled() {
return canceled;
}
}
package concurenta1;
import java.util.concurrent.*;
public class EvenChecker implements Runnable {
//task-ul EvenChecker depinde de un obiect non-task
//se elimina problema rezultatelor inconsistente (race condition)
//ordinea de incheiere a task-urilor nu este garantata
private IntGenerator generator;
private final int id;
public EvenChecker(IntGenerator g, int ident) {
generator = g;
id = ident;
}
//resursa partajata de fiecare thread este obiectul generator
public void run() {
//flag-ul de terminare al task-urilor
//este gestionat la nivelul obiectului partajat generator
//generator.isCanceled trimite semanulul de terminare catre toate
//thread-urile care utilizeaza acel obiect
while (!generator.isCanceled()) { //monitorizarea resursei - flag-ul is canceled
int val = generator.next();
if (val % 2 != 0) { //daca val este impar - se opreste executia
System.err.println(val + " not even!");
generator.cancel(); // Cancels all EvenCheckers
}
}
}

// Test any type of IntGenerator:


public static void test(IntGenerator gp, int count) {
System.out.println("Press Control-C to exit");
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < count; i++) {
exec.execute(new EvenChecker(gp, i)); //toate thread-urile folosesc acceeasi resursa obiectul gp
}
exec.shutdown();
}
// Default value for count:
public static void test(IntGenerator gp) {
test(gp, 10);
}
}

package concurenta1;
public class EvenGenerator extends IntGenerator {
private int currentEvenValue = 0;
public int next() { //next trebuie intotdeauna sa returneze un numar par
//este posibil ca un task deschis sa apeleze next() si sa modifice
//valoarea currentEvenValue
//imediat dupa ce un alt task a facut prima operatie de increment
//=> obiectul poate fi intr-o stare incorecta iar next() poate returna
//un nr impar
// aici trebuie inserat cuvantul cheie synchronized pentru serializa accesul la resursa
// intrebare: care este resursa critica?
++currentEvenValue; // Danger point here!
++currentEvenValue;
return currentEvenValue;
}
public static void main(String[] args) {
EvenChecker.test(new EvenGenerator());
}
}

Pentru a rezolva problema accesului concurrent la obiectul de tip IntGenerator fara ca nici un fir
de executie sa poata sa acceseze o stare incorecta a obiectului Intgenerator. (nu se stie cand
un Thread este lansat de catre scheduller).

Rezolvare: blocarea resursei de catre primul thread care o acceseaza (aceasta devenind
inaccesibila celorlalte thread-uri pana cand procesul care a blocat-o o va elibera)
Sincronizarea in EvenGenerator pentru rezolvarea problemei de mai sus:
package concurenta1;
public class SynchronizedEvenGenerator extends IntGenerator {
private int currentEvenValue = 0;
//prin synchronized - primul task care apeleaza
//metoda next() va bloca zona de memorie aferenta
//atributului currentEvenValue - nici un thread nu va \
//putea accesa zona currentValue - pana cand nu va fi eliberata
public synchronized int next() {
++currentEvenValue;
Thread.yield(); // Cause failure faster
++currentEvenValue;
/*if (currentEvenValue%2!=0) //verificare ca se genereaza doar numere pare
System.err.println(currentEvenValue+" is not even!"); */
return currentEvenValue;
}
public static void main(String[] args) {
EvenChecker.test(new SynchronizedEvenGenerator());
}
}

Utilizarea de obiecte de tip Lock pentru a rezolva problema excluderii mutual


public class MutexEvenGenerator extends IntGenerator {
private int currentEvenValue = 0;
// obiect de tip ReentrantLock folosit pe post de monitor
private Lock lock = new ReentrantLock();
public int next() {
lock.lock(); // se marcheaza inceputul zonei critice
try {
++currentEvenValue;
++currentEvenValue;
return currentEvenValue;
} finally {
lock.unlock(); // unlock trebuie pus in finally

}
}
public static void main(String[] args) {
EvenChecker.test(new MutexEvenGenerator());
}
} ///:~

5. Utilizarea sectiunilor critice sincronizarea doar a unor sectiuni specifice de cod nu a


intregii metode:
Blocul synchronized{..}
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.*;
class Pair { // Not thread-safe
private int x, y;
public Pair(int x, int y) {
this.x = x;
this.y = y;
}
public Pair() {
this(0, 0);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void incrementX() {
x++;
}
public void incrementY() {
y++;
}
public String toString() {
return "x: " + x + ", y: " + y;
}
//clase inner de tip exceptie
public class PairValuesNotEqualException
extends RuntimeException {

public PairValuesNotEqualException() {
super("Pair values not equal: " + Pair.this);
}
}
// Arbitrary invariant -- both variables must be equal:
public void checkState() { //ambele valori x si y trebuie sa fie egale
if (x != y) {
throw new PairValuesNotEqualException(); //daca x nu e egal cu y =>exceptie
}
}
}
// Protect a Pair inside a thread-safe class:
abstract class PairManager {
AtomicInteger checkCounter = new AtomicInteger(0);
//!!!obiectul atribut p este partajat de mai multe
//fire de executie
//obiectul p trebuie intodeauna sa isi pastreze starea
//de consistenta: adica p.x==p.y
protected Pair p = new Pair();
//lista sincronizata
private List<Pair> storage = Collections.synchronizedList(new ArrayList<Pair>());
//metoda sincronizata pentru a pastra starea obiectului corecta - valorile x si y egale
// atentie: se returneaza o copie a obiectului p
// intrebare: de ce trebuie synchronized?
public synchronized Pair getPair() {
// Make a copy to keep the original safe:
return new Pair(p.getX(), p.getY());
}
// Assume this is a time consuming operation
protected void store(Pair p) {
storage.add(p);//adaugarea perechii la storage
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException ignore) {
}
}
public abstract void increment();
}
// Synchronize the entire method:
class PairManager1 extends PairManager {
//metoda sincronizata pentru a ne asigura
//ca cele doua perechi x si y sa ramana egale
//astfel incat nici un alt fir de executie
//sa nu poata modifica in paralel pe x si y din p

public synchronized void increment() {


p.incrementX();
p.incrementY();
store(getPair());
}
}
// Use a critical section:
class PairManager2 extends PairManager {
public void increment() {
Pair temp;
//se sincronizeaza doar partea de cod care
//face modificarea valorilor pe obiectul
//partajat p - pentru ca p sa isi pastreze starea corecta
//zona critica
synchronized (this) {
p.incrementX();
p.incrementY();
temp = getPair();
}
store(temp);
}
}
//task ce genereaza perechi utilizand clasa PairManager
class PairManipulator implements Runnable {
private PairManager pm;
public PairManipulator(PairManager pm) {
this.pm = pm;
}
//task ce apeleaza metoda increment din clasele Pair
//metodele increment sunt sincronizate => generarea perechilor corect
public void run() {
while (true) {
pm.increment();
}
}
public String toString() {
return "Pair: " + pm.getPair()
+ " checkCounter = " + pm.checkCounter.get();
}
}
//task ce verifica perechi - manipulate de un PairManager
class PairChecker implements Runnable {
private PairManager pm;
public PairChecker(PairManager pm) {
this.pm = pm;

}
//un alt thread monitorizeaza in paralel
//daca perechea trimisa in constructor este in stare valida
public void run() {
while (true) {
pm.checkCounter.incrementAndGet(); //de cate ori s-a facut verificarea
pm.getPair().checkState(); //verificarea starii perechii pm
}
}
}
public class CriticalSection {
// Test the two different approaches:
static void testApproaches(PairManager pman1, PairManager pman2) {
ExecutorService exec = Executors.newCachedThreadPool();
//pair manipulators - task-uri ce genereaza perechi pe baza obiectelor PairManager
PairManipulator pm1 = new PairManipulator(pman1);
PairManipulator pm2 = new PairManipulator(pman2);
//task-uri ce manipuleaza perechi utilizand obiectele pairManager
PairChecker pcheck1 = new PairChecker(pman1);
PairChecker pcheck2 = new PairChecker(pman2);
exec.execute(pm1);//task pentru pm1 - generare a perechilor prin obiectul pm1
exec.execute(pm2); //task pentru pm2 - generare a perechilor prin obiectul pm2
exec.execute(pcheck1); //task pentru verificarea perechilor - pt cele generate prin pm1
exec.execute(pcheck2);//task pentru verificarea perechilor - pt cele generate prin pm2
try {
TimeUnit.MILLISECONDS.sleep(500); //suspendarea thread-ului pentru 500 milisecunde
} catch (InterruptedException e) {
System.out.println("Sleep interrupted");
}
System.out.println("pm1: " + pm1 + "\npm2: " + pm2);
System.exit(0);
}
public static void main(String[] args) {
PairManager pman1 = new PairManager1();
PairManager pman2 = new PairManager2();
testApproaches(pman1, pman2);
}
}

Vous aimerez peut-être aussi