Kihagyás

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 Thread osztály sleep metó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 átadott String
    • <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: Date object getTime(), vagy System.currentTimeMillis() metódus)

Egy példa futás eredménye:

Konzol
demo 0 69562
demo 1 70562
demo 2 71562
...

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:

Konzol
elso 0 54796
masodik 0 55281
elso 1 55796
masodik 1 56296
elso 2 56796
masodik 2 57296
...

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 kapott String-et berakja a Fifo-ba. Ha a Fifo tele 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 a Fifo-ból törli a legöregebb elemet. Ha a Fifo ü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 Producer kap egy Fifo-t is.
  • A <szoveg> <szam> üzenetet tegye be a Fifo-ba put metó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 Consumer szálként tudjon futni.
  • A konstruktorban kap egy f Fifo-t, egy s String-et és egy n int-et.
  • A Consumer az f get-jét használva String-eket olvas. Az olvasást követően a standard outputra írja ki: consumed <s> <olvasott string> <ido>
  • Ezután az n paramé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 a wait() hívások while ciklusban (nem pedig if feltételben) szerepelnek, így a felébredő szál mindig újraellenőrzi a puffer állapotát!
  • Ébresztéskor notify() helyett használd a notifyAll() 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 és Consumer osztályokat (vagy készíts SingleProducer / SingleConsumer osztá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 (execute metó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.png Producer osztály go() metódussal, kimenet másodpercenként.
  • f2.png Két Producer szál fél másodperc különbséggel.
  • f3.png Fifo osztály és a Producer produced … kimenete.
  • f4.png Consumer szál és a consumed … kimenet egy Producerrel.
  • f5.png Eltérő várakozási idejű Producer és Consumer (és a javított algoritmus).
  • f6.png Runnable-t implementáló Producer a 4. feladat szerint kipróbálva.
  • f7.png 3 Producer és 4 Consumer együtt futtatva.
  • f8.png Fifo put/get metódusnevet és szálazonosítót kiíró kimenete.
  • f9.png Kiegészítő feladat: volatile stopSignal, interrupt(), setDaemon(), join() és CopyOnWriteArrayList.
  • f10.png (iMSc) ScheduledThreadPoolExecutor 10-10 szállal, 15-15 feladattal.
  • Java Kód A megírt, fordítható és futtatható Java forráskódok megnyitva az IDE-ben (Neptun-kóddal).