Problema filozofilor

Enuntul problemei
Mai muti filozofi stau la o masa rotunda. Un filozof face doar doua lucruri: gandeste si mananca. El gandeste pentru o vreme, dupa care i se face foame. Mananca pana se satura si apoi incepe sa gandeasca din nou. Fiecare filozof este atat de implicat in activitatea sa de a gandi sau manca incat este total neatent la ce se intampla in jurul sau. Intre 2 filozofi se afla un singur betigas pentru mancat. Un filozof are nevoie si de betigasul din dreapta si de cel din stanga pentru a putea manca. Evident, 2 filozofi vecini nu pot manca in acelasi timp. Un filozof nu-l poate deranja pe vecinul sau. Daca un filozof vrea sa manance si un vecin de-al sau are unul dintre cele doua betigase, el trebuie sa astepte pana cand betigasul respectiv devine disponibil.

Algoritmul problemei

Pentru rezolvarea acestei problemei voi implementa pentru fiecare filozof in parte cate un fir de executie.
Un algoritm simplu de aplicat pentru fiecare filozof ar fi urmatorul:
       1. gandeste;
       2. ia betigasul din dreapta;
       3. ia betigasul din stanga;
       4. mananca;
       5. lasa betigasul din stanga;
       6. lasa betigasul din dreapta;
       7. reia procedeul.
In acest algoritm apare insa o problema serioasa, si anume: daca toti filozofii ridica betigasul din dreapta in acelasi timp, cand vor incerca sa ridice betigasul din stanga, nu va fi acolo. Va fi la vecinul din stanga. Astfel toti filozofii vor muri de foame cu un betigas in mana si cu mancarea pe masa. Acest lucru se numeste deadlock (impas).

In problemele care necesita implementarea mai multor fire de executie, asa cum este cea de fata, deadlocks sunt un mare pericol. Ele pot aparea din unul din urmatoarele motive:
- fiecare fir (filozof) are nevoie exclusiva de cele doua betigase;
- unui filozof nu ii este permis sa ia betigasul de la vecinul lui;
- fiecare filozof asteapta in timp ce tine un betigas pe care un alt filozof il asteapta.
Primele doua motive nu pot fi inlaturate, deci ramane doar al treilea. Deadlocks pot fi inlaturate daca exista un betigas anume pe care nici un filozof nu-l poate tine in timp ce asteapta celalalt betigas. Asadar orice filozof poate manca cu acest betigas, dar nu-l poate tine fara sa manance. Pentru aceasta voi marca un betigas ca fiind "aur", iar celelatle ca fiind "lemn". Filozofii ridica un betigas, apoi pe celalalt. Daca betigasul "aur" este ales primul, este lasat jos imediat si este ales celalalt. Este posibil ca in timpul dintre lasarea acestuia jos si ridicarea celuilalt, acesta din urma sa fie cel de aur. In acest caz, se reia algoritmul. Dar iata care este algoritmul folosit:
       1. gandeste;
       2. ridica betigasul din dreapta;
       3. daca betigasul din dreapta este marcat "aur", atunci
              3.1. begin
              3.2. lasa jos betigasul din dreapta;
              3.3. ridica betigasul din stanga;
              3.4. daca betigasul din stanga este marcat "aur", atunci
                     3.4.1. lasa jos betigasul din stanga;
                     3.4.2. treci la pasul 2.
              3.5. (altfel) apuca betigasul din dreapta;
              3.6. end;
       4. altfel apuca betigasul din stanga;
       5. mananca;
       6. inverseaza betigasele;
       7. lasa jos betigasul din mana dreapta;
       8. lasa jos betigasul din mana stanga;
       9. treci la pasul 1.
Dupa ce mananca, fiecare filozof trebuie sa inverseze betigasele. Acest lucru acorda sanse egale tuturor. Altfel, cel din stanga ar fi dezavantajat.
Pentru ca filozofii sa nu interfereze unul cu altul, voi folosi o regiune critica pentru a ma asigura ca un filozof are posibilitatea de a ridica sau de a lasa jos un betigas. In timp ce un filozof ridica sau lasa jos un betigas, veinul lui nu are voie sa atinga betigasul. Daca un filozof vrea sa ridice un betigas, dar acesta nu este disponibil, el trebuie sa astepte pana va fi disponibil. Toate acestea se realizeaza prin sincronizarea firelor de executie.

Programul include:
1. clasa "Philosopher" care mosteneste clasa "Thread" pentru implementarea cate unui fir de executie pentru fiecare filozof in parte. Aici este suprascrisa functia run() care apeleaza functiile definite tot aici think(), PickUpSticks(), eat(), PutDownSticks(),
2. clasa "Chopstick" pentru desenarea betigaselor;
3. clasa "ScenePainter" pentru desenarea celor 5 stari ale filozofilor (gandeste, asteapta betigasul din dreapta, are betigasul din dreapta si asteapta betigasul din stanga, betigasul din dreapta a fost marcat "aur", mananca);
4. alte clase pentru afisare grafica.


1