Programozási nyelvek és módszerek Java Thread-ek Laki Sándor lakis@inf.elte.hu 2006. május 3. 0-0
Szálak (Threads) Ahhoz, hogy egy mai rendszer m ködhessen több 10-100 folyamatnak kell futnia. A folyamatok futtatása ütemezést igényel, mivel egy gépben általában egy processzor van. Ekkor az operációs rendszernek kell ütemeznie azok futását. Ahhoz, hogy egy folyamat futását megszakítsuk, hogy egy másik futhasson, ahhoz az adott folyamatot folytatható állapotba kell helyezni, le kell menteni a változóit. Ezek m veletek nem segítik a folyamatok futását, overhead-nek nevezzük az ilyen fajta - a végrehajtás szempontjából nem hasznos - m veleteket. Ütemezéskor két ellentétes stratégiát feltételez igényt kell kielégíteni. Minél gyorsabban fussanak a folyamatok: ebb l a szempontból az lenne a legjobb, ha minden folyamat végigfutna, miel tt újat indítanánk. Minél simábban fussanak a folyamatok: a felhasználó nem szeretné észrevenni, hogy nem csak az fut, amivel foglalkozik, 0-1
pedig hát... Ebb l a szempontból minél gyakrabban kellene váltogatni a folyamatokat Az ütemez nek e két stratégia között kell optimumot találnia, és persze ne felejtsük el, hogy az ütemez is egy folyamat! 0-2
Processzek - Szálak Amikor egy operációs rendszer process-eket futtat, akkor ezek külön memóriaterületen futnak, (esetleg osztott memóriát használnak). A szálak hasonlóképpen tudnak az operációs rendszeren párhuzamosan futni. Abban különböznek a process-ekt l, hogy azonos memóriaterületein futnak, ezért el tudják olvasni egymás változóit. Persze ezt meg kell oldani. Például mp3 lejátszáskor külön szál tömöríti ki a fájlt egy puerbe, amib l a másik szál kiolvassa és lejátssza az adatokat. Hogyan áll le egy szál? Vagy mond le a futás jogáról, vagy az ütemez dönti el, hogy ez a szál most már ne fusson (preemptív). Java-ban nem lehet tudni, hogy az ütemez hogyan m ködik, az operációs rendszerre van bízva. 0-3
Itt feltételeztük, hogy egymástól függetlenül futnak. Ez nincsen mindig így, van, amikor egymásra vagy er forrásra várnak (blokkolt állapot tipikusan I/O m velet esetén) végül minden szálnak véget ér a futása. (stop) 0-4
Szál létrehozása 1. út: Leszármaztatunk a java.lang.thread oszutályból, és felüldeniáljuk a public void run() metódusát! public class MyThread extends Thread { public MyThread() {super(); public void run() { while (true) System.out.println(Futok!!!); Ezzel deniáltuk a szálat leíró osztályt! 0-5
Szál létrehozása 1.út: Szál létrehozása és elindítása: MyThread first = new MyThread(); MyThread second = new MyThread(); // Ezzel még nem futnak, meg el kell indítanunk. first.start(); second.start(); 0-6
Szál létrehozása 2. út: Megvalósítjuk java.lang.runnable interfészt, és ezzel paraméterezve hozunk létre egy Thread objektumot! public class MyThread implements Runnable { public MyThread() { public void run() { while (true) System.out.println(Futok!!!); Ezzel deniáltuk a szálat leíró osztályt! 0-7
Szál létrehozása 2.út: Szál létrehozása és elindítása: MyThread first = new MyThread(); MyThread second = new MyThread(); // Ezek még nem szálak... // szálak létrehozása: Thread t1 = new Thread( first ); Thread t2 = new Thread( second ); t1.start(); t2.start(); Megjegyzés: Lehetne így is: (new Thread( first )).start(); 0-8
Szál befejez dése Amikor a a run metódus végére ér. Ekkor ún. dead állapotba kerül. A GC csak a dead állapotban lev szál objektumokat szabadíthatja fel. 0-9
Feladat: Írj olyan osztályt, ami implementálja a Runnable interfészt. Konstruktorában paraméterként megkap egy Stringet, a szál nevét. Majd ezt végtelen ciklusban írja a képerny re. Hozz létre két szálat: Jancsit és Julcsát. 0-10
Feladat: Legyen adott: public class Tarolo { static int adat; /** Creates a new instance of Tarolo */ public Tarolo() { Készíts két szálat: Mindegyik növelje a Tarolo.a értékét egyesével egészen 1000000-ig. Az eredményt irasd ki a képerny re! Mi a probléma? 0-11
1. átírás: Írjunk a tárolónak inkrementáló fv.-t! public static void increment(){ int i = adat; try { Thread.sleep(300); catch(exception e){ adat = i + 1; System.out.println(i+1); // A try-catch ide azért k Mi itt a probléma? 0-12
2. átírás: Megoldás: synchronized kulcsszó Egy objektum szinkronizált metódusai közül csak egy lehet aktív, és ez is csak egy példányban futhat! Ezt szokás monitornak nevezni. A szinkronizált metódusok között kölcsönös kizárást(mutual exclusion) bíztosít. synchronized public static void increment(){ int i = adat; try { Thread.sleep(300); catch(exception e){ adat = i + 1; System.out.println(i+1); 0-13
Egyébb lehet ségek: sleep( id ) - Adott ideig felfüggeszti a szál végrehajtását.(semmi sem garantálja, hogy nem ébred fel el bb.)(elaltatás) wait() - Szál felfüggesztése, amíg valaki ki nem adja rá a notify v. a notifyall hívást.(blokkolás) wait( id ) - Adott ideig vár, hogy valaki megszólítsa.(blokkolás) notify() - Szál újra aktivizálása.(felszabadítás) notifyall() - Minden várakozó szál felszabadítása. interrupt() yield() - Szól az ütemez nek, hogy lemond a futás jogáról. setpriority( érték ) - Szál prioritása. 0-14
Feladat(Termel - fogyasztó): Hozz létre egy tároló osztályt! Ebben egy int-et lehessen tárolni. Legyen egy put ill. egy get m velete az elem berakásához és kivételéhez. Hozz létre két szálat: Az egyik generáljon random értékeket. És rakja ezeket a tárolóba. A másik vegye ki az elemeket a tárolóból. Oldd meg, hogy üres tárolóból ne lehessen kivenni elemet, ill. tárolóba ne lehessen betenni! tele 0-15
Kocsma-szimulátor: Hozd létre a következ osztályokat: Kocsma, Részeg, melyeket a runnable megvalósításásval kapsz. Írj kocsma szimulátort! A részegek (szálak) a kocsmába egy ajtó taskon keresztül tudnak bemenni. A kocsmában csak öt ember fér el, ennél több nem mehet be az ajtón. Azonban hét részeg próbál bejutni. Aki bejutott, megiszik egy sört 10 mp alatt, majd kimegy, és rögtön megy vissza. Aki nem jut be, az alszik egyet a parkban 5 mp-ig, majd újra próbálkozik. A szimulációban írd ki, hogy melyik részeg mit csinál! 0-16
Lock objektumok! A java.util.concurrent.locks csomagban van egy Lock ill. egy Condition interfész. A Lock egy megvalósítása a ReentrantLock. Ezt fogjuk használni. Példa: Író-Olvasó probléma import java.util.concurrent.*; public class BoundedBuffer<Data> { private Data buffer[]; private int first; private int last; private int numberinbuffer; private int size; private Lock lock = new ReentrantLock(); private final Condition notfull = lock.newcondition(); private final Condition notempty = Lock.newCondition(); 0-17
public BoundedBuffer(int length) { size = length; buffer = (Data[]) new Object[size]; last = 0; first = 0; numberinbuffer = 0; 0-18
public void put(data item) throws InterruptedException { lock.lock(); try { while (numberinbuffer == size) notfull.await(); last = (last + 1) % size; numberinbuffer++; buffer[last] = item; notempty.signal(); finally { lock.unlock(); 0-19
public Data get() throws InterruptedException { lock.lock(); try { while (numberinbuffer == 0) notempty.await(); first = (first + 1) % size ; numberinbuffer--; notfull.signal(); return buffer[first]; finally { lock.unlock(); 0-20