Gyakran észrevesszük, hogy a programok futtatásakor vannak bizonyos korlátok. Például egyszerre általában csak egyetlen telepítőprogram futhat egy operációs rendszeren. Vagy amíg fut egy program korábbi verziójának eltávolítása, addig nem futhat a program új verziójának telepítője. Vagy egy nagyobb erőforrás igényű program (periféria meghajtó program, képernyő videó+hang rögzítő, hardveres gyorsítást használó játékprogram) egyszerre csak egy példányban indítható el. Előfordulhat kategóriánkénti korlát is, például a különböző víruskereső programok általában „nem tűrik meg” egymást, kizárólagosságot „követelnek”.
Lássunk példát arra, hogyan kell készíteni egy példányban futó Java programot!
Néhány dolgot át kell gondolni:
- Amikor először indítjuk el a programot, akkor olyan egyedi dolgot kell beállítani, ami mindvégig úgy marad, amíg a program fut. Ezt megtehetjük a memóriában, de megfelelő jogosultsággal futtatva a programot akár beleírhatunk a Windows rendszerleíró adatbázisába (Registry) is. Előbbi módszer platformfüggetlen lenne – ahogyan egy Java programhoz illik –, és az utóbbi megoldás pedig operációs rendszertől függne.
- Amikor többedszer (második, harmadik… példányban) indítjuk el a programot, akkor ezt az egyedi dolgot észlelni kell és meg kell akadályozni a program másodszori, harmadszori elindítását. Hasznos, ha ezekben az esetekben kapunk hibaüzenetet, például: „This application is already running”.
- Amikor a programot szabályosan állítjuk le, akkor a korábban beállított egyedi dolgot semmissé kell tenni. Ez biztosítja, hogy a program egymás után – egymással nem párhuzamosan, egymástól függetlenül – elindítható lesz.
A megoldás két részből áll. Ez a Java forráskód első része:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
private static ServerSocket ss=null; static { try { ss=new ServerSocket(65001, 10, InetAddress.getLocalHost()); } catch(UnknownHostException e) { ; } catch(IOException e) { JOptionPane.showMessageDialog(null, "This application is already running.", PROGRAM_STUDENT_TITLE, JOptionPane.ERROR_MESSAGE); System.exit(0); } } |
A program indulásakor le kell futni a fenti forráskódnak. A static blokk a konstruktor előtt hajtódik végre (például a modell vagy a nézet rétegben). A java.net csomag kötetlen ServerSocket osztályú ss nevű objektumát kell inicializálni helyben ( InetAddress.getLocalHost()) egy nem dedikált porttal ( 65001). Ez elsőre mindig sikerült és az objektum „beül a memóriába” egy nem blokkoló elven működő háttérszálon. Ha (többedszerre) nem sikerül létrehozni az objektumot, akkor – kezelve a kötelezően kezelendő kivételeket – hasznos jelezni ezt logban, konzolon vagy felbukkanó párbeszédablakban és a programból ki kell lépni (másképpen: a duplikált futtatását meg kell akadályozni).
Ez a Java forráskód második része:
1 2 3 4 5 6 7 8 |
try { //a program csak 1 példányban futhat if(ss!=null && !ss.isClosed()) ss.close(); System.exit(0); } catch(IOException e) { ; } |
A programból való szabályos kilépéskor le kell futni a fenti forráskódnak. Ez ellenőrzést követően lezárja az ss objektumot és kilép a programból. Például a main() metódusban, ha elfogynak az utasítások egy konzolos alkalmazásban, vagy GUI-s programban nyomógombra kattintás actionPerformed() esemény, vagy (fő)ablak bezárásának kísérlete WindowClosing() esemény.
A programot érdemes körültekintően tesztelni. Ha elrontjuk a fenti felsorolásban vázolt logikai működés végrehajtásának sorrendjét, akkor fejlesztés vagy tesztelés közben akár a számítógépet is újra kell indítanunk.
A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.
A Java EE szoftverfejlesztő tanfolyamunkon, a szakmai modul 5-8. óra Szálkezelés, párhuzamosság alkalommal megismerjük a megoldás elméleti hátterét és a 17-24. óra Socket és RMI alapú kommunikáció alkalommal többféle megvalósítást is kódolunk, tesztelünk.
Balázs: az egyik korábbi Java EE órán beszéltünk arról, hogy a chatprogramba beépíthető olyan korlát, hogy a szobákban hányan tudjanak belépni. Megpróbálnám megoldani ezt. Továbbgondolva Sándor fix korlát=1 megoldását módosítható korlát=n-re. Jó ötlet hozzá a
ServerSocket
használata?András: ha a programodban minden szobában ugyanaz (statikus) a korlát, akkor a
ServerSocket
jó megoldás. Ha szobánként különböző korlátokat szeretnél, vagy esetleg a futás közben szeretnéd beállíthatóvá tenni a korlátot, akkor arra a jövő heti órán nézünk egy praktikusabb (dinamikus) megoldást.Addig is gondold át szálkezeléshez kötődő projektjeinket (a teljes Java forráskódokat az ILIAS-on találod).
Sándor: az egyszerre egy példányban futó programhoz hasonló problémának gondolom azt, ha egy processz sokáig tart és meg kell várni, amíg befejeződik, mielőtt újra elindítható. Adódik egy olyan időszelet, amikor egy példányban megy a végrehajtás. Oké ez így?
Helyesen gondolod Bende. Az ILIAS-ban megtalálod az operációs rendszerek ütemezési problémáinál a szemaforkezelést. Az elv azonos azzal, amit leírtál. Ugyanott találsz hozzá mintaprogramokat is. A megvalósítás többféleképpen is lehet.
Egy típusfeladat erre: egy mappa méretének kiszámítása. Ez több másodpercig szokott tartani. Konzolosan privilegizált szálkezeléssel célszerű megoldani. Ha GUI-s a program, akkor letiltjuk az ezt a funkciót indító nyomógombot, hogy amíg fut, addig ne legyen újraindítható ugyanarra a mappára. Ehhez kivételkezelés is kell, mert mindenképpen folytatódnia kell tudnia a programnak.
Köszönöm a tippet. A konzolos változat már megy. Várom a jövő hónapi GUI-s órákat.