Rezultatul execuției: afișarea mesajului par de 4 ori, afișarea 
	  mesajului impar de 3 ori, afișarea mesajului după atribuire, 
	  afișarea mesajului impar de 3 ori, afișarea mesajului OK,afișarea 
	  mesajului impar de 3 ori.
	 
 Limitări ale paralelismului: Limbajul Java este conceput pentru 
	  a suporta programarea concurentă. Fără îndoială, caracterul de multithreading 
	  al aplicațiilor este dezvoltat tot mai mult în industria soft. Pe lângă 
	  avantajele oferite de astfel de aplicații, apar și unele puncte delicate 
	  care se cer cu grijă tratate de către programatori.
	 
 Siguranța: Când thread-urile nu sunt complet independente, în 
	  timpul execuției, fiecare poate influența celelalte thread-uri. Pentru a 
	  evita acest lucru, obiectele de tip thread pot folosi mecanismele de sincronizare 
	  sau tehnici de excluziune care să prevină intercalarea execuțiilor. Utilizarea 
	  thread-urilor multiple, implicând obiecte proiectate pentru a lucra în mod 
	  secvențial, conduce la programe greu de citit și greu de depanat.
	 
 Timpul de viață: Activitățile din programele concurente se pot 
	  opri pur și simplu, dintr-o varietate de motive. De exemplu din cauză că 
	  alte activități consumă cicluri CPU sau din cauză că 2 activități diferite 
	  sunt blocate, fiecare așteptând pe cealaltă pentru a continua.
	 
 Nedeterminismul: Activitățile cu caracter multithread pot fi intercalate 
	  în mod arbitrar. Nici un program nu rulează identic la 2 execuții. Activitățile 
	  ce necesită un mare volum de calcule se pot întrerupe înainte ca aceste 
	  calcule să fie satisfăcute. Acest lucru face ca programele multithreading 
	  să fie greu de înțeles, de depanat și greu predictibile. Construcția unui 
	  thread, setarea lui și utilizarea metodelor determină un consum de memorie 
	  mai ridicat decât folosirea obiectelor normale.
	 
 Sincronizarea: Metodele Java implicate în sincronizare sunt mai 
	  lente decât cele care nu beneficiază de protecția oferită de sistemul de 
	  sincronizare.
	 
 Fire de execuție (Thread-uri)
	  Thread-urile pot fi folosite în 2 moduri. Prima metodă constă în implementarea 
	  unei interfețe Runnable. Exemplu: 
	 
public class Simple implements Runnable{ 	 
	  protected String message;
 	  protected TextArea text; 	 
	  //constructorul clasei
 	  public Simple (String m, TextArea t){
 	  message = m;
 	  text = t;
 	  } 	 
	 public void run(){
 	  text.appendText(message);
 	  }
 	  } 	 
	 Obiect de tip Runnable: Clasa Simple implementează o interfață 
	  de tip Runnable. Această interfață are doar o singură metodă run(), 
	  nu are argumente la intrare și nu întoarce rezultate. 
	 
public interface Runnable{
 	  public void run();
 	  } 	 
	 Interfețele sunt mai abstracte decât clasele, deoarece ele nu spun nimic 
	  despre reprezentare sau cod. Ele descriu doar semnătura (nume, argumente 
	  și tipuri de rezultate) ale operațiilor publice. Clasele ce implementează 
	  interfața Runnable nu au nimic în comun exceptând folosirea unei 
	  metode run(). Versiunea secvențială a programului: 
	 
public class Sapplet extends Applet{
 	  protected TextArea text;
 	  protected Simple hello;
 	  protected Simple bye; 	 
	  public Sapplet(){
 	  text = new TextArea(4, 40); // 4 randuri si 40 coloane
 	  hello = new Simple("Hello\n", text);
 	  bye = new Simple("Bye\n", text);
 	  }
 	  public void init(){
 	  add(text);
 	  } 	 
	 public void start(){
 	  hello.run();
 	  bye.run();
 	  }
 	  } 	 
	 Clasa Sapplet va fi executată secvențial. După invocarea metodei start(), 
	  se va continua cu rularea primului obiect (hello) până la execuția completă. 
	  După ce programul revine din execuție prin instrucțiunea return din metoda 
	  run(), se începe rularea celui de-al doilea obiect (bye). Prin urmare, în 
	  fereastra TextArea vor apărea cele două mesaje succesiv și complet separate. 
	  În acest exemplu, nu avem o aplicație multithreading, execuția celor două 
	  obiecte de tip Runnable fiind făcută în mod secvențial.
	 
 Versiunea multithreading: A doua cale de folosire a interfeței 
	  Runnable constă în crearea unui nou thread folosind metoda new Thread(Runnable 
	  x) Exemplu: 
	 
public class TApplet extends Applet{
 	  protected TextArea text;
 	  protected Simple hello;
 	  protected Simple bye; 	 
	  public TApplet(){
 	  text=new TextArea(4,40);
 	  hello=new Simple("Hello\n", text);
 	  bye=new Simple("Bye\n", text);
 	  } 	 
	  public void init(){
 	  add(text);
 	  } 	 
	 public void start(){
 	  new Thread(hello).start();
 	  new Thread(bye).start(); 
 	  }
 	  } 	 
	 Observație: Metoda care este apelată în thread este start(). Thread.start() 
	  determină execuția metodei Runnable.run() Se știe că obiectul de tip applet 
	  deține și el o metodă start() care nu are nici o legătură cu metoda cu aceași 
	  nume din Thread.
	 
 Sincronizarea: Când 2 sau mai multe thread-uri accesează același 
	  obiect, ele pot interfera.
	 
 Instrumentul principal, pentru evitarea acestei interferențe, este mecanismul 
	  de sincronizare. Principalul merit al sincronizării este asigurarea că un 
	  singur thread obține accesul la un obiect într-un moment dat. Exemplu:
	 
 Dacă java.awt.TextArea nu ar fi fost implementat folosind sincronizarea, 
	  am fi putut face un mic program de ajutor care asigură că un singur thread 
	  execută TextArea.appendText() la un moment dat. 
	 
Class Appender{
 	  private TextArea text;
 	  public Appender(TextArea t){
 	  text=t;
 	  } 	 
	 synchronized void append(String s){
 	  text.appendText(s);
 	  }
 	  } 	 
	 public class ThreadApplet extends Applet{
 	  protected TextArea text;
 	  protected Appender appender;
 	  protected Simple hello;
 	  protected Simple bye; 	 
	 public ThreadApplet(){
 	  text=new TextArea(4,40);
 	  appender=new Appender(text);
 	  hello=new Simple("Hello\n",appender);
 	  bye=new Simple("Bye\n",appen
 	  der);
 	  } 	 
	 public void init(){
 	  add(text);
 	  } 	 
	 public void start(){
 	  new Thread(hello).start();
 	  new Thread(bye).start();
 	  } 	 
	 } 	 
	 public class Simple{
 	  protected Appender appender;
 	  protected String message; 	 
	  public Simple (String m, Appen
 	  der a){
 	  message=m;
 	  appender=a;
 	  } 	 
	 public void run(){
 	  appender.append(message);
 	  }
 	  } 	 
	 Metodele de control ale thread-ului: 
	   start(): determină apelarea 
	  metodei run() ca o activitate independentă. Fără o instrucțiune specială, 
	  cum ar fi stop(), rularea thread-ului se termină când metoda run() întoarce 
	  return.
	   isAlive(): întoarce true dacă un thread a fost startat dar nu a fost încă 
	  terminat.
	   stop(): termină irevocabil activitatea unui thread. Nu are loc omorârea 
	  thread-ului, doar activitatea îi este stopată. Deci metoda start() poate 
	  fi din nou apelată pentru același obiect de tip Thread.
	   suspend(): oprește temporar thread-ul ce va continua execuția după invocarea 
	  metodei resume().
	   sleep(): determină suspendarea execuției unui thread pentru un timp dat 
	  în milisecunde. Thread-ul poate să nu continue imediat după timpul dat dacă 
	  există alt thread activ.
	   interrupt(): determină întreruperea instrucțiunilor sleep(), wait() cu 
	  o InterruptException care poate fi prinsă, captată și prelucrată într-un 
	  mod specific aplicației. 
	 
Priorități: Cazul fericit al rulării programelor multithreading 
	  este cazul multiprocesor. Majoritatea mașinilor disponibile la ora actuală 
	  pe piață au un singur procesor, acesta prin capacitatea de efectuare a unui 
	  număr tot mai mare de operații pe secundă putând face destul de bine fată 
	  aplicațiilor multithreading. Un thread este runnable, dacă a fost startat 
	  dar nu a fost terminat, suspendat , blocat și nu este angajat într-o instrucțiune 
	  wait().
	 
 Când nu sunt în rulare, thread-urile sunt în așteptare într-o coadă, 
	  aranjată în funcție de priorități. Această coadă este gestionată de sistemul 
	  de rulare Java. Prioritățile pot fi schimbate prin apelul instrucțiunii 
	  Thread.setPriority cu un argument cuprins între Thread.MIN_PRIORITY și MAX_PRIORITY. 
	  Instrucțiunea Thread .yield recapătă controlul pentru un thread de prioritate 
	  egală cu celelalte. Dacă o metodă nu este marcată ca synchronized, 
	  atunci poate fi executată imediat ce este apelată, chiar în timp ce altă 
	  metodă sincronizată rulează. Calificativul de synchronized nu poate 
	  fi transferat în subclase. O subclasă trebuie declarată explicit synchronized, 
	  altfel ea va fi tratată ca una obișnuită. 
	 
 Metodele declarate în interfețele Java nu pot fi înzestrate cu calificativul 
	  de syncronized. După cum se știe, metodele din interfețe nu furnizează 
	  informații cu privire la cod, ele sunt propriu-zis o semnătură a metodei 
	  (specifică tipul argumentelor de intrare și tipul de date întors de metodă). 
	  Aceste metode trebuie suprascrise de către programator. Un exemplu de metodă 
	  interfață este metoda run din interfața Runnable.
	 
 Wait și Notification: Ca urmare a executării instrucțiunii wait(): 
	 
 thread-ul curent este suspendat
	   sistemul de rulare Java plasează thread-ul într-o coadă de așteptare internă 
	  și inaccesibilă programatorului.
	 
Ca urmare a executării instrucțiunii notify():
	   Din coada de așteptare internă este scos în mod arbitrar un thread.
	   Acest thread trebuie să obțină blocajul de sincronizare pentru obiectul 
	  țintă, care întotdeauna va determina blocarea cel puțin pană thread-ul va 
	  chema metoda notify . 
	   Thread-ul este atunci reluat din punctul unde apare metoda wait.
	 
Invocarea metodei notifyAll:
	   Invocarea metodei notifyAll lucrează în același mod ca și notify numai 
	  că pașii de mai sus se aplica la toate thread-urile ce așteaptă în coada 
	  de așteptare pentru obiectul țintă.
	   Două versiuni alternative ale metodei wait preia, argumente specificând 
	  timpul maxim de așteptare în coadă. Dacă timpul de așteptare este depășit, 
	  atunci metoda notify este invocată automat.
	   Dacă o instrucțiune interrupt apare în timpul execuției unei instrucțiuni 
	  wait, același mecanism notify se aplică exceptând controlul întors către 
	  clauza catch asociată cu invocarea lui wait.
	 
Aplicație
	 Structura aplicației: Ca o ilustrare a noțiunilor prezentate mai 
	  sus, este listat codul unei aplicații multithreading. 
	  Interfața cu utilizatorul este dată de 3 butoane numite first, second, 
	  third. În spațiul de deasupra lor, 3 thread-uri care rulează concurent 
	  afișează numere consecutive de la 0 la 9. De observat că afișarea are un 
	  caracter ciclic, după cifra 9 urmând cifra 0. Cifra de pornire nu este 0, 
	  ci este generată printr-un generator Java de numere aleatoare și este diferită 
	  în cazul fiecărui thread. Procesul de afișare este întrerupt prin apăsarea 
	  butonului corespunzător fiecărui thread, caz în care este apelată metoda 
	  thread.stop(). Reîmprospătarea periodică a ecranului și captarea acțiunii 
	  asupra butoanelor sunt asigurate de un al patrulea fir de execuție din programul 
	  principal. Acest thread se va opri când toate celelalte thread-uri sunt 
	  oprite. 
	 
 Ca și structură, programul definește o clasă (Machine) ce extinde clasa 
	  Thread. Această clasă are următoarele atribute:
	   initial_value cifra inițială generată aleator, de la care se începe afișarea 
	  cifrelor.
	   counter variabila ce va fi afișată și care ia valori de la 0 la 9, în 
	  mod ciclic.
	   x_draw și y_draw coordonatele la care o instantiere a acestei clase va 
	  începe să afișeze.
	 
Metoda Thread.isAlive() testează starea firului de execuție care a apelat 
	  metoda. 
	 
Considerații finale: 
	 Pentru cei interesați câteva sugestii de îmbunătățire a aplicației. Ar 
	  fi utilă înzestrarea aplicației cu un buton start care să repornească întregul 
	  joc de la început. De asemenea, ar fi interesant dacă utilizatorul ar putea 
	  să fixeze rapiditatea derulării cifrelor pe ecran pentru fiecare thread 
	  în parte, între niște valori fixate de programator. 
	 
Bibliografie:
	  Doug Lea 
	  Concurrent Programming 
	  în Java- Design Principles and Patterns
	  Addison-Wesley 1996