Labor 07 - Java Thread labor¶
Bevezető és Előkészületek¶
Készítette: László Zoltán (2015) és Goldschmidt Balázs (2016), BME IIT.
A feladatok megoldásához felhasználandó osztályok (Thread, Object, stb.) leírásait az alábbi URL-en találod meg: http://download.oracle.com/javase/8/docs/api/
- Projekt neve:
ThreadLabor - Java csomag (package):
hu.bme.iit.prog3.threads - Beadás módja: helyszíni bemutatás (
f1.png–f10.png)
Nincs feltöltés! (Lokális ellenőrzés)
Ezen a laboron nincs GitHub vagy Moodle feltöltés! A feladatokhoz tartozó képernyőképeket közvetlenül a gépeden (ajánlottan a ThreadLabor projekt gyökerébe) mentsd el f1.png, f2.png, stb. néven.
Minden képernyőképen látszódjon a Neptun-kódod kommentként a forráskódban! A laborvezető a gépednél fogja ellenőrizni a megoldást.
Alapfeladatok¶
1. Producer osztály¶
Készíts egy Producer osztályt! Az osztálynak legyen egy void go() metódusa, amely végtelen ciklusban másodpercenként üzenetet ír ki a standard outputra, a konstruktorában pedig vegyen át egy String-et.
A kipróbáláshoz készíts egy Application osztályt, aminek a main metódusa (a program belépési pontja) elindít egy Producer-t!
Korlátozások:
- A várakozást a
Threadosztálysleepmetódusával valósítsd meg, mert így az objektum nem tartja magánál a CPU-t, nem történik foglalva várakozás (busy-wait). - A kiírt üzenet a következő formátumú legyen:
<szoveg> <szam> <ido>, ahol:<szoveg>: az objektum konstruktorában átadottString<szam>: a kiírás sorszáma (0-ról indul)<ido>: a kiírás ideje (a millisec-ben mért rendszeridő utolsó 5 jegye, javaslat:DateobjectgetTime(), vagySystem.currentTimeMillis()metódus)
Egy példa futás eredménye:
BEMUTATANDÓ KÉPERNYŐKÉP
Készíts egy képernyőképet, amelyen látszik a Producer és az Application kódja (Neptun-kóddal kommentben), valamint a konzol a másodpercenként kiírt üzenetekkel.
Fájlnév a projekt gyökerében: f1.png. A laborvezető a gépednél ellenőrzi!
2. Producer osztály szállal¶
Módosítsd úgy a fenti Producer-t, hogy önálló szálként tudjon futni! A két tanult lehetőség közül használd az öröklést (extends Thread)!
Módosítsd úgy az Application osztályt, hogy a main metódus fél másodperc különbséggel elindít két Producer szálat!
Emlékeztető az előadásból: start() vs. run()
Minden szál örököl egy void start() metódust, amely elvégzi a szükséges inicializálást, majd külön szálon futtatja a run() metódust. Maga a run() csak a belépési pont, közvetlenül meghívva nem indít új szálat!
Egy példa futás eredménye:
BEMUTATANDÓ KÉPERNYŐKÉP
Készíts egy képernyőképet a szálként futó Producer és az Application kódjáról, valamint a konzolról, ahol a két szál fél másodperc különbséggel írja ki az üzeneteket.
Fájlnév a projekt gyökerében: f2.png. A laborvezető a gépednél ellenőrzi!
3. Fifo puffer¶
Készíts egy maximum 10 elemű, String-eket tároló, konkurens működésű Fifo osztályt, amelynek metódusai:
void put(String): A paraméterként kapottString-et berakja aFifo-ba. Ha aFifotele van, akkor a futtató szál korlátlan ideig várakozik és csak akkor ébred fel, ha a puffer tartalma változik.String get(): Visszaadja és aFifo-ból törli a legöregebb elemet. Ha aFifoüres, akkor a futtató szál korlátlan ideig várakozik és csak akkor ébred fel, ha a puffer tartalma változik.
Javaslat: a tároláshoz használd a LinkedList<String> osztályt.
A Fifo puffer legyen szálbiztos, vagyis egy időben csak egy szál futtathassa a metódusait (synchronized)! Minden kötelezően kezelendő kivételt (InterruptedException) adj tovább a hívónak! Ne felejtsd el, hogy a várakozással (wait()) és értesítéssel (notify() / notifyAll()) kapcsolatos metódusokat csak akkor szabad meghívni, ha a futó szálnak kizárólagos futtatási joga van a használt objektumra (belépett az objektum monitorába).
Módosítsd a 2. feladatban készült Producer osztályt az alábbiak szerint:
- A konstruktorban a
Producerkap egyFifo-t is. - A
<szoveg> <szam>üzenetet tegye be aFifo-baputmetódussal! - A
Fifo-ba történő beírást követően a standard outputra írja ki:produced <szoveg> <szam> <ido>
BEMUTATANDÓ KÉPERNYŐKÉP
Készíts egy képernyőképet a Fifo osztály kódjáról és a produced … kimenetet mutató konzolról.
Fájlnév a projekt gyökerében: f3.png. A laborvezető a gépednél ellenőrzi!
4. Consumer osztály¶
Készíts egy Consumer osztályt a Fifo végtelen ciklusban történő olvasására!
- A
Consumerszálként tudjon futni. - A konstruktorban kap egy
fFifo-t, egysString-et és egynint-et. - A
Consumerazfget-jét használvaString-eket olvas. Az olvasást követően a standard outputra írja ki:consumed <s> <olvasott string> <ido> - Ezután az
nparaméternek megfelelő ideig alszik, majd újra kezdi a ciklust.
Módosítsd az Application main-jét, amelyben létrehozol egy Fifo-t, majd külön-külön szálon elindítasz egy-egy Producer-t és Consumer-t!
BEMUTATANDÓ KÉPERNYŐKÉP
Készíts egy képernyőképet a Consumer osztály kódjáról és a konzolról, ahol látszanak a produced és a consumed sorok.
Fájlnév a projekt gyökerében: f4.png. A laborvezető a gépednél ellenőrzi!
5. Sebességarányok¶
Módosítsd úgy a Producer-t, hogy konstruktor paraméterként meg lehessen adni a várakozási idejét! Indítsd a Producer és a Consumer objektumokat eltérő várakozási értékekkel! Mit tapasztalsz? Ha hibát észlelsz, javítsd az algoritmust.
BEMUTATANDÓ KÉPERNYŐKÉP
Készíts egy képernyőképet a paraméterezett Producer kódjáról és az eltérő sebességű futás konzolos kimenetéről.
Fájlnév a projekt gyökerében: f5.png. A laborvezető a gépednél ellenőrzi!
6. Szálkezelés delegálással¶
A Producer-t írd át úgy, hogy a Thread-ből való származtatás helyett a Runnable interfészt implementálja (implements Runnable)! Próbáld ki az alkalmazást a 4. feladat szerint (indítás: new Thread(producer).start())!
BEMUTATANDÓ KÉPERNYŐKÉP
Készíts egy képernyőképet a Runnable implementációról és az alkalmazás konzolos kimenetéről.
Fájlnév a projekt gyökerében: f6.png. A laborvezető a gépednél ellenőrzi!
7. Több termelő és fogyasztó¶
Az Application-ben példányosíts több Producer-t és Consumer-t! Legyen 3 db Producer 1000-1000 ms várakozási idővel, és 4 db Consumer 100-100 ms várakozási idővel. Indítsd őket közvetlenül egymás után! Mit tapasztalsz? Ha problémát észlelsz, akkor javítsd a hibásan implementált algoritmust!
Fix várakozási idők helyett próbálkozhatsz véletlen időkkel is. Véletlen szám generálásához használd a Math.random() metódust, vagy a java.util.Random osztályt!
Tipp: Miért dobhat kivételt vagy fagyhat le több szálnál?
- Győződj meg róla, hogy a
Fifo-ban await()hívásokwhileciklusban (nem pedigiffeltételben) szerepelnek, így a felébredő szál mindig újraellenőrzi a puffer állapotát! - Ébresztéskor
notify()helyett használd anotifyAll()metódust, amely az összes várakozó szálat felébreszti!
BEMUTATANDÓ KÉPERNYŐKÉP
Készíts egy képernyőképet az Application kódjáról és a 3 Producer + 4 Consumer együttes futásának konzolos kimenetéről.
Fájlnév a projekt gyökerében: f7.png. A laborvezető a gépednél ellenőrzi!
8. Szálak jellemzői¶
Módosítsd a Fifo osztályt az alábbiak szerint! A Fifo osztályban mind a put, mind a get metódus írja ki a metódus nevét, valamint a metódust futtató szál azonosítóját! Használd a Thread osztály metódusait (Thread.currentThread().getId(), getName())!
BEMUTATANDÓ KÉPERNYŐKÉP
Készíts egy képernyőképet a kibővített Fifo kódjáról és a metódusneveket és szálazonosítókat kiíró konzolról.
Fájlnév a projekt gyökerében: f8.png. A laborvezető a gépednél ellenőrzi!
iMSc Feladat (1 iMSc pont)¶
10. Szálak ütemezett indítása Executor-ral¶
Készítette: Goldschmidt Balázs, BME IIT, 2016.
- Módosítsd úgy a
ProducerésConsumerosztályokat (vagy készítsSingleProducer/SingleConsumerosztályokat), hogy ne ciklikusan fussanak, hanem egyszeri futás után álljanak le. - A leállás előtt az alvási paraméterben megadott ideig hajtsanak végre alvást (ezzel imitáljuk a hosszabb ideig tartó futást).
- Hozz létre egy-egy
ScheduledThreadPoolExecutor-t a producerek és a consumerek elindításához. A pool-okban 10-10 szálnak legyen hely. - Ütemezd a végrehajtást úgy (
executemetódus), hogy az executoroknak producer és consumer objektumokat adsz (15-öt külön-külön mindkettőnek). - A végrehajtási idők és az indítási periódusok legyenek olyanok, hogy 10 szál végrehajtása tovább tartson, mint 10 szál együttes újraindítási ideje (pl. 2000 ms alvási idő a szálakban, 100 ms szünet a beküldések között).
- Figyeld meg a működést!
BEMUTATANDÓ KÉPERNYŐKÉP (1 iMSc pont)
Készíts egy képernyőképet az Executor alapú ütemező kódjáról és a konzolos kimenetről, ahol megfigyelhető a 10 szálas pool telítődése és a 11–15. feladatok késleltetett lefutása!
Fájlnév a projekt gyökerében: f10.png. A laborvezető a gépednél ellenőrzi!
Lokális beadási ellenőrzőlista¶
Pipáld ki az elkészült feladatokat (az állapotot a böngésződ automatikusan elmenti)! Ha minden megvan, szólj a laborvezetőnek, aki a gépednél fogja ellenőrizni a fájlokat:
-
f1.pngProducer osztálygo()metódussal, kimenet másodpercenként. -
f2.pngKét Producer szál fél másodperc különbséggel. -
f3.pngFifo osztály és a Producerproduced …kimenete. -
f4.pngConsumer szál és aconsumed …kimenet egy Producerrel. -
f5.pngEltérő várakozási idejű Producer és Consumer (és a javított algoritmus). -
f6.pngRunnable-t implementáló Producer a 4. feladat szerint kipróbálva. -
f7.png3 Producer és 4 Consumer együtt futtatva. -
f8.pngFifoput/getmetódusnevet és szálazonosítót kiíró kimenete. -
f9.pngKiegészítő feladat:volatile stopSignal,interrupt(),setDaemon(),join()ésCopyOnWriteArrayList. -
f10.png(iMSc)ScheduledThreadPoolExecutor10-10 szállal, 15-15 feladattal. -
Java KódA megírt, fordítható és futtatható Java forráskódok megnyitva az IDE-ben (Neptun-kóddal).