Procházka na provázku Program s metodami připomíná knósský labyrint se sály, jimiž se protlouká statečný Theseus, jemuž pro šťastný návrat chytrá Ariadna dala klubíčko. Theseus vždy při vstupu do metody vlákno rozvíjí a při návratu z metody svíjí. V metodách a konstruktorech provádí příkazy. V jávský labyrint je multithreadový čili se v něm potuluje ( i rekurzivně ) najednou více postav: Hrdinové i démoni ( tj. služební džinové ) – každý z drží jeden konec svého vlákna. Druhé konce třímá JVM v roli Ariadny, aby je v případě nehod ( Throwable ) mohla z labyrintu vytáhnout – pokud se ovšem vlákno nepřetrhlo – to, aby v něm zranění a padlí nezůstali. ( Leč v jednom procesoru se v daném okamžiku pohybuje nanejvýš jeden – tak jako v “člověče nezlob se“. ) Jakmile všichni hrdinové opustí labyrint, JVM vypudí i démony, zruší labyrint a sebe ( JVM = Ariadna spáchá sebevraždu ) – tím aplikace skončí. Nesmutněme, získali jsme výsledky i zkušenosti a můžeme ji reinkarnovat. PJV06
Osud state state state state state BLOCK completed interrupt( ) false isAlive( ) true BLOCK state completed interrupt( ) I/O end sleep(…) join(…) I/O start CPU scheduler start( ) run( ) {…} RUNNABLE state RUNNING state NEW DEAD interrupt( ) yield( ) interrupt( ) sets flag only sets flag only synchronized wait(…) acquires object’s lock must have lock then releases it notify( ) notifyAll( ) timeout interrupt( ) LOCK state WAIT state synchronization object’s lock pool synchronization object’s wait pool PJV06
Přidělování času CPU time scheduler JVM scheduler přiděluje CPU čas nespecifikovaným způsobem některému vláknu s nejvyšší prioritou, pokud ono je ve stavu runnable. priority T8 T6 MAX 10 CPU time scheduler NORM 5 T7 T5 T2 MIN 1 T3 T4 T9 T6 T10 T1 vlákna ve stavu: NEW RUNNABLE RUNNING BLOCKED DEAD PJV06
Vlákno = thread = lightweight process = task ... Vláknem je pouze objekt typu java.lang.Thread. Vlákno není program – je to jakýsi výpočtář, tj. objekt vykonávající činnost. K provedení výpočtu je nezbytné alespoň jedno vlákno, které má: k dispozici program – tedy předpis postupu ( metody, konstruktory ), přístup k datům ( atributy tříd, objektů, parametry, proměnné ) příděly času na práci. Vlákno si musí dynamicky pamatovat: v které metodě či konstruktoru se právě nalézá a kam se pak vrátit, své lokální proměnné ( obé je v aktivačním záznamu na zásobníku ), adresu aktuálního příkazu v aktuální metodě ( program counter ). Výpočet končí, skončí-li všechna nedémonická vlákna. Délku vlákna lze chápat jako momentální počet aktivačních záznamů na zásobníku. PJV06
java.lang: Thread a Runnable JVM scheduler dodává vláknu CPU čas skrze metodu run( ) objektů typu Thread, zařazených do prioritních front, jsou-li ve stavu running. V těle metody public void run( ) se definuje požadované chování vlákna – přičemž lze volat též metody libovolného objektu či třídy. Interfejs Runnable obsahuje pouze metodu: public void run( ); Thread je konkrétní třída implementující Runnable “skoro prázdnou“ metodou umožňující předat řízení metodě run v cílovému objektu takto: Runnable target ; // initialized in constructor eventually public void run( ) { if ( target != null ) target.run( ); } V potomcích Threadu se metoda run( ) překrývá a v ní je určena požadovaná činnost. PJV06
Metoda run( ) Metoda public void run( ) { … } v typu Thread definuje činnost vlákna. Volá ji JVM - ne programátor – ten jen vlákno odstartuje metodou start( ), čímž JVM přidá vlákno do stromu vláken a do fronty na CPU čas. Tím se vlákno oživí – metoda public final boolean isAlive( ) pak vrací true. Metoda run( ) typicky obsahuje cyklus, který probíhá, pokud jiné vlákno nenastaví condition na false. Tak vlákno dokoná práci definovaným způsobem ( zavrženou metodou stop( ) nikoli ). class MyThread extends Thread { boolean condition = true; // počáteční nastavení public void run( ) { while ( condition ) { // požadovaná činnost } // případný epilog } // smrtící závorka nebo return kdekoli v metodě PJV06
Třída Thread má přetížené konstruktory s kombinacemi těchto parametrů: Runnable target - přesměrování dodávky CPU do určeného objektu, který implementuje interfejs Runnable. String name - jméno vlákna ( chybí-li, dosadí se systematické jméno ). ThreadGroup group - přiřazení vlákna do skupiny, změna nemožná. Skupinu vláken lze ovládat najednou – viz třída ThreadGroup. long stackSize - nastavení velikosti zásobníku. Má vnitřní enum Thread.State pro stavy v rámci JVM (ordinal 0 .. 5): NEW, RUNNABLE, BLOCKED – čeká na zámek, WAITING – čeká na akci, TIME_WAITING – čeká dočasně na akci, TERMINATED. Má vnitřní interfejs Thread.UncaughtExceptionHandler definující void uncaughtException( Thread t, Throwable e ) Nedémon defaultně vytváří nedémona, démon démona. Démoničnost je nastavitelná jen před spuštěním dotyčného vlákna. PJV06
Statické metody Thread currentThread( ) - odkaz k běžnému vláknu. int activeCount( ) - počet aktivních vláken ve skupině tohoto vlákna. native boolean holdsLock( Object o ) - má vlákno zámek objektu ? boolean interrupted( ) - bylo toto vlákno přerušeno? A výmaz příznaku. native void sleep( long msec ) throws InterruptedException – uspání. Při msec < 0 vyhodí IllegalArgumentException. Při přerušení spánku jiným vláknem vyhodí výjimku. native void yield( ) - vlákno přejde ze stavu RUNNING do RUNNABLE, tj. zřekne se zbytku momentálního přídělu času. int enumerate ( Thread[ ] tarr ) – do určeného pole zkopíruje všechna aktivní vlákna skupiny běžného vlákna a jejích podskupin. PJV06
Nestatické metody void run( ) - definuje funkcionalitu. void start( ) - aktivace, tj. oživení ( mrtvé vlákno nelze oživit ). final native boolean isAlive( ) – je už či ještě živé ? long getId( ) – stálý identifikátor vlákna > 0 ( znovupoužitelný ) final String getName( ) / void setName( String name ) – ovládání jména. final int getPriority( ) / void setPriority( int priority ) - ovládání priority. Thread.State getState( ) – stav - pro monitoring - ne pro synchronizaci. ThreadGroup getThreadGroup( ) – skupina do které vlákno patří. boolean isDaemon( ) - test démona. void setDaemon( boolean on ) - nastavení démona/nedémona. void interrupt( ) - přerušit ono vlákno, záleží na stavu. boolean isInterrupted( ) - bylo ono vlákno přerušeno? Příznak nemění. final void join( … ) throws InterruptedException - čekání na konec jiného vlákna, případně s vymezením času. PJV06 ALG
Metody sledování vlákna static void dumpStack( ) - výpis zásobníku tohoto vlákna do System.err . static Map<Thread, StackTraceElement[ ]> getAllStackTraces( ) - mapa všech živých vláken StackTraceElement[ ] getStackTrace( ) – pole prvků zásobníku void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler h) Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler( ) Thread.UncaughtExceptionHandler getUncaughtExceptionHandler( ) void setContextClassLoader ( ClassLoader cl ) - nastavení PJV06 ALG
Způsoby použití S vlákny lze pracovat dvojím způsobem: Definovat potomka třídy Thread, v něm překrýt metodu run( ) pro požadovanou funkcionalitu, zkonstruovat objekt a ten aktivovat zděděnou metodou start( ). Toto řešení se hodí jen pro velmi jednoduché případy. 2. Definovat nějakou třídu implementující interfejs Runnable a v ní překrýt metodu run( ) pro žádanou funkcionalitu. Zkonstruovat příslušný objekt. Zkonstruovat objekt Thread s parametrem Runnable odkazující na onen objekt. Pak odstartovat objekt Thread. Toto řešení je obecnější, Runnable objekt ( jenž však není vláknem ) může být složitý, neboť může výhodně dědit od nějaké bohaté třídy. PJV06
1. způsob: objekt Tik – je vláknem class Tik extends Thread { @Override // překrytí prázdné metody třídy Thread public void run( ) { try { while( true ) { System.out.println( " Tik " ); sleep( 1000 ); // static method } } catch( InterruptedException ex ) { } spuštění zkráceně: new Tik( ).start( ); spuštění rozepsaně: Tik t = new Tik( ); t.start( ); Protože Tik je Thread, startem se zařadí do fronty na CPU čas. PJV06
2. způsob: objekt Tak – není vláknem class Tak extends Orloj implements Runnable { @Override // překrytí abstraktní metody interfejsu Runnable public void run( ) { try { while( true ) { System.out.println( " Tak " ); Thread.sleep( 1000 ); // static method } } catch( InterruptedException ex ) { } spuštění zkráceně: new Thread( new Tak( ) ). start( ); spuštění rozepsaně: Runnable r = new Tak( ); new Thread( r ). start( ); Objekt Tak není vláknem, ač má metodu run ( tu však nelze odstartovat ), - volá ji nepřekrytá metoda run Threadu, neb objekt Tak je jejím targetem. PJV06
CPU time priority queue Tik a Tak Tik is Thread Thread has Tak java.lang. Object java.util .concurrent java.lang. any. ForkJoin WorkThread Thread Orloj CPU time priority queue java.lang. run() { } static sleep() Tik Thread start() Runnable run() ; my. my. call return Tik Tak Tak run() {…} run() {…} class vlákno nevlákno interface extends implements PJV06
Interakce vláken Vlákna mohou běžet nezávisle, mohou se vzájemně hledat, testovat, [ne]koordinovaně ovlivňovat či mohou spolupracovat a to metodami pro: hledání: static: currentThread( ), activeCount( ), holdsLock(…) , … getThreadGroup( ), ... testování: static: interrupted( ) isAlive( ), isDaemon( ), isInterrupted( ), getState(), … ovlivnění: start( ), nastavení proměnných, interrupt( ), setPriority( … ), yield( ), setName( … ), ... Následují metody využívají služby synchronizačního monitoru: respekt: synchronized - klíčové slovo definuje kritickou sekci naváznost: join( … ) - na ukončenou činnost jiného vlákna spolupráci: wait( … ) - čekání na znovu přidělení zámku, notify( ) - nespecifické upozornění čekajícímu vláknu, notifyAll( ) resp. všem čekajícím vláknům PJV06
Virtuální interrupt vlákna pojednávají metody interrupt( ), isInterrupted( ) a static interrupted( ). Volání t . interrupt( ) provede akce podle stavu vlákna t : RUNNABLE / RUNNING : Jen nastaví vláknu t příznak přerušení. WAIT / join / sleeping : Vláknu t zruší příznak přerušení, změní mu stav na další tj. LOCK / RUNNABLE / RUNNING, což pak ve stavu RUNNING hned vyhodí InterruptedException. not alive : no op. IO blocked / Selector : jen pro nio channels – viz javadoc. Vlákno může přerušit samo sebe, pokud ho lze modifikovat, což ověří metoda checkAccess( ). Zamítnutí vede na SecurityException. Volání t . isInterrupted( ) nemění příznak přerušení, vrátí true je-li nastaven, jinak ( i pro not alive ) false. Volání Thread.interrupted( ) zruší příznak přerušení běžného vlákna, vrátí true byl-li předtím nastaven, jinak ( i pro not alive ) false. PJV06
Kritická sekce (1/2) je ta část programu v níž současná aktivita více vláken na témže objektu by mohla způsobit nekonzistenci dat. Nad tím bdí tzv. monitor coby klíčník. ( Obdobou je železniční autoblok, zpovědnice či uzamykatelná toaleta. ) Kritickou sekcí je metoda či blok označená modifikátorem synchronized . Vlákno do ní vstoupí získá-li zámek od určeného synchronizačního objektu a tím jiné vlákno nemůže do metody vstoupit a Objekt se však nezamyká ( má mít modifikátor private). Každý objekt ( i pole i class-objekt ) má právě jeden nesdílitelný zámek ( intrinsic či monitor lock ) Určení synchronizačního objekt, jehož zámek se užije: - pro instanční metody je to this objekt, - pro třídní metody je to popisný objekt třídy: Třída.class . - v bloku to explicitně určí programátor. Běžící vlákno, jež zámek nezíská, monitor přeřadí do stavu LOCK a to do lock-poolu synchronizačního objektu kde vyčká na přidělení onoho zámku. Kritické sekce lze i do sebe vkládat. Vlákno může tak získat i více zámků. PJV06
Kritická sekce (2/2) Když vlákno kritickou sekci opustí, monitor mu zámek odebere a přidělí ho nederministicky některému vláknu čekajímu v lock poolu a jeho stav změní na RUNNABLE. Vlákno jež je v kritické sekci, leč nemůže žádanou operaci zatím provést, zavolá metodu wait( [ timeout ] ) čímž mu monitor odebere zámek a přeřadí ho do stavu WAIT a to ve wait-poolu synchronizačního objektu, kde čeká na notifikaci, timeout či interupt po čemž je přeřazen do stavu LOCK a do v lock-poolu synchronizačního objektu. Tam vyčká na přidělení onoho zámku a přeřazení do stavu RUNNABLE. Teprve ve stavu RUNNING se vlákno vrátí z metody wait, leč znovu musí testovat zda už lze tu operaci vykonat, ne-li pak opět zavolá metodu wait. V kritické sekci může být tedy současně více vláken, ale jen jedno aktivní. Kritické sekce mají být co nejkratší - kvůli průchodnosti programu. PJV06
Synchronizace class X { ... synchronized ... method1 ( ... ) { /* Synchronizace na this objekt */ } … method2 ( ... ) { … // Nesynchronizovaná metoda synchronized( o1 ) { // Synchronizované bloky synchronized( o2 ) { // o1, o2 refererují nějaké objekty … // či this či Z.class či pole } … Atributy jejichž hodnoty se mění mají být private. Konstruktory a inicializátory provádí právě jedno vlákno a tedy synchronizace netřeba. PJV06
Osud state state state state state BLOCK completed interrupt( ) false isAlive( ) true BLOCK state completed interrupt( ) I/O end sleep(…) join(…) I/O start CPU scheduler start( ) run( ) {…} RUNNABLE state RUNNING state NEW DEAD interrupt( ) yield( ) interrupt( ) sets flag only sets flag only synchronized wait(…) acquires object’s lock must have lock then releases it notify( ) notifyAll( ) timeout interrupt( ) LOCK state WAIT state synchronization object’s lock pool synchronization object’s wait pool PJV06
Metoda final synchronized void join( … ) využívá vnitřně metodu final native void wait(…) ke známé fintě: já si tu počkám až ty (to) doděláš ( tj. trpělivě vyčkám jen určitý čas ) . Volání ty.join( [ timeout ] ); blokuje běžné vlákno ( já ), pokud je vlákno ty živé, nejdéle však po udanou dobu, je-li vlákno ty ještě neživé anebo již mrtvé, neblokuje se vlákno( já ) . Je-li běžné vlákno ( já ) netrpělivé, nedočká výsledku práce vlákna ty . Skutečný stav se musí zajistit programaticky. Čekající vlákno ( já ) může vytrhnout z čekání nějaké jiné vlákno ( ty / on ) metodou já.interrupt( ) jež vyhodí InterruptedException. Na ukončení vlákna může postupně čekat více vláken. Pro timeout == 0 je doba ∞ , tudíž join( ) je ekvivalentní join( 0 ). Vlákna se nijak vzájemně nenavazují ( tj. nesloučí se v nějaké jediné – neb každé samostatně doběhne ), navazují se jejich práce. PJV06
Story: pilný strýc a líný synovec Synovec lajdá ( je blokován ), než strýc dokončí své dílo ( vydělat miliony ) - čili až zemře. Žádný z nich si však mamonu dlouho neužije. Je-li synovec nedočkavý, zavolá join( timeout ) - získá jen část peněz. Spuštění : new Nephew( new Uncle( ) ); public class Nephew extends Thread { Uncle u; public Nephew( Uncle u ) { this.u=u; this.start( ); } public void run( ) { try { u.join( ); } catch( InterruptedException ex ) { } // blocked System.out.println( u.money ); // heritage } class Uncle extends Thread { int money = 0; public Uncle( ) { this.start( ); } public void run( ) { while ( ++money < 100000000 ); } // work PJV06
Zmatek v hanebné bance public class PhonyBank { public static void main( String[ ] args ) { new Clerk( ).start( ); new Clerk( ).start( ); // tito dva pilní úředníci for ( int i=1; true; i++ ) System.out.println( i + ".:" + Accounts.dump( ) ) ; } class Accounts { static long a1=0 , a2 = 1000; // s dvěma účty zmatkují static void move( int x ) { a1 -= x; a2 += x; } // a škodí si navzájem static String dump( ) { return a1+ " + " +a2+ " = " +( a1+a2 ); } class Clerk extends Thread { public void run( ) { while ( true ) Accounts.move( ( int ) ( ( Math.random( ) - 0.5 ) *100 ) ); PJV06
Synchronizace v solidní bance FairBank zjedná nápravu tím, že transakce s účty dělají postupně: class Accounts { static private long a1 = 0, a2 = 1000; static synchronized void move( int x ) { a1 -= x; a2 += x; } // OK static synchronized String dump( ) { return a1+ “+" +a2+ “=" +( a1+a2 ); } } anebo takto: static void move( int x ) { synchronized ( Accounts.class ) { a1 -= x; a2 += x; } // OK Jak patrno v obou případech se (nevhodně) využívá zámek třídy Accounts. PJV06
PC-problém ( Producer – Consumer ) Bezpečný a poctivý sklad hmotných objektů je takovýto: Nelze se do něj vloupat ( proměnné musejí být private ). Žádný objekt se nesmí ztratit ani podstrčit. Tentýž objekt nelze odebrat dvakrát. Má dvě vyhrazené brány: PUT - pro producenty objektů, GET - pro konsumenty objektů. Petenti tj. producenti a konsumenti: - přicházejí v náhodných časech, - vzájemně se neznají a nekomunikují, - jen jeden petent může vstoupit dovnitř, pokud tam není žádný aktivní, - nevpuštění petenti čekají venku na další příležitost, - uvnitř čekající petenti jsou upozorňováni na změnu stavu skladu. Sklad má danou kapacitu a vykazuje krajní stavy: - plný - pak vpuštěný producent čeká, - prázdný - pak vpuštěný konsument čeká. * Provoz usměrňuje pan Monitor – takový komisní vrátný, který otvírá brány petentům, bere jim zámky a zavírá je do čekáren. PJV06
PC - problem put get Store Monitor lock Producers Consumers private synchronized put synchronized get Lock pool Wait pool PJV06
Monitor a metody wait a notify Monitor působí jako prostředník mezi čekajícími a notifikujícími vlákny, neboť ta se vůbec nestýkají. Monitor ovlivňují generální public final native void metody: wait ( ) - odebere vláknu zámek, zavede ho do čekárny Wait-pool wait( long timeout ) - totéž, časem uvolní vlákno do čekárny Lock-pool notify( ) - uvolní nějaké vlákno notifyAll( ) - uvolní všechna vlákna interrupt( ) - ( vznikne InterruptedException ) Volat je však může jen vlákno držící zámek, tj. jen uvnitř kritické sekce, jinak dojde k IllegalMonitorStateException. PJV06
Producer Consumer Problem public class PCProblem { public static void main( String[ ] args ) throws Exception { Box b = new Box( ); Producer p = new Producer( b ); p.start( ); Consumer c = new Consumer( b ); c.start( ); p.join( ); c.join( ); b.print( "END" ); } PJV06
Producer Consumer Problem class Box { // Box for one deposit only private Object x; public void print( String r ) { System.out.println( r+" Box has "+x ); } synchronized void put( Object z ) { // z must not be null while ( x!=null ) try { this.wait( ); } catch ( InterruptedException ex ) { } this.notifyAll( ); x = z; print( Thread.currentThread().toString() ); } synchronized Object get( ) { while ( x==null ) try { this.wait( ); } catch ( InterruptedException ex ) { } Object z = x; x = null; return z; PJV06
Producer Consumer Problem class Producer extends Thread { Box b; Producer( Box b ) { this.b=b; } public void run( ) { for ( int i = 0; i < 10; i++ ) { b.put( new Integer( i ) ); try { sleep( ( int )( Math.random( )*1000 ) ); } catch ( InterruptedException ex ) {…} } PJV06
Producer Consumer Problem class Consumer extends Thread { Box b; Consumer( Box b ) { this.b=b; } public void run( ) { for ( int i = 0; i < 10; i++ ) { System.out.println( ( Integer ) b.get( ) ); try { sleep( ( int ) ( Math.random( )*1000 ) ); } catch ( InterruptedException ex ) { } } PJV06
Uvíznutí (deadlock) Dva chudí lešetínští kováři v mají jedno společné nářadí: kladivo a kleště. Nedohodnou-li se, dojde časem k zlobně umrtvujícímu nicnedělání, tzv. deadlocku čili smrtelnému zaklesnutí. Přihodí se to takto: První uchopil kladivo a potřebuje ještě kleště. Mezitím však druhý uchopil kleště a čeká až bude kladivo volné. Nikdo to za ně nevyřeší a tak oba čekají a čekají ... čímž živnosti uvíznou a oba ještě více zchudnou - a šafářovic Andulka se nedočká. Přitom stačí rozumná dohoda - budeš-li potřebovat oba nástroje: Nejdříve uchop kladivo a teprve pak sháněj kleště. Pracuj. Pak nejdříve pusť kleště, potom kladivo. Ta zaručuje momentálnímu držiteli kladiva, že kleště budou časem volné. Kovářů může být ve městě i více. PJV06
Proti uvíznutí Programátor znemožní uvíznutí takto: synchronized ( hammer ) { synchronized ( tongs ) { ... // work } Více nářadí lze vložit do kolekce synchronizované implicitně ( např. Vector ) anebo explicitně synchronizačním obalem ( např. ArrayList ) anebo do pole. Pole jsou synchronizované implicitně. private Collection tools = new Vector( ); tools.add( hammer ); tools.add( tongs ); tools.add(...); // fill tools synchronized ( tools ) { Object x = tools.get(...); PJV06
Strom vláken tvoří objekty třídy ThreadGroup, jenž obsahuje všechna živá vlákna, ThreadGroup( ThreadGroup parent, String name ) – konstruktor. int activeCount( ) – odhad počtu vláken ve skupině. int activeGroupCount( ) – odhad počtu skupin ve skupině. int enumerate( Thread[ ] ) – výčet vláken ve skupině. int enumerate( ThreadGroup[ ] ) – výčet skupin vláken ve skupině. ThreadGroup getParent( ) – reference k nadskupině. boolean parentOf( ThreadGroup g ) – test příslušnosti k podstromu g. void list( ) – výpis skupiny. void interrupt( ) – přerušení všech vláken podstromu. boolean isDaemon( ) – je to démonická skupina ? void setDaemon( boolean on ) – démonizace všech vláken skupiny. void setMaxPriority( int pri ) – nastavení pro další členy skupiny. void destroy( ) – zrušení prázdného podstromu. void uncaughtException( Thread t, Throwable e ) – odchyt nechyceného PJV06
Strom vláken aplikace system main main 5 MyThreadA 5 MyThreadB 5 Reference Handler 10 main 5 Finalizer 8 MyGroup MyThreadA 5 Signal Dispatcher 9 MyThreadB 5 Attach Listener 5 thread group daemon PJV06
Ukončení běhu vlákna Normální: metoda run objektu typu Thread skončí návratem do JVM. Vlákno je mrtvé tj. metoda isAlive( ) vrací false. Nelze ho oživit. Objekt je vyřazen z fronty na CPU čas i ze stromu vláken, jeho reference ke skupině je nastavena na null – je bezprizorný. Pokud existuje reference, objekt je dostupný. Abnormální: následkem neodchyceného objektu ( Error nebo RuntimeException ). Vlákno může definovat Thread.UncaughtExceptionHandler, jinak se zavolá metoda uncaughtException( … ) definovaná v typu ThreadGroup. Ta defaultně volá tutéž metodu v rodičovském objektu. Metody uncaughtException … volá JVM. Násilné: pomocí zavržené metody stop( ), což je [sebe]vražda následkem ThreadDeath chyby, což může vést k nedozírným následkům. Doporučuje se nepoužívat. PJV06
Časové spouštění úloh umožňují třídy java.util.Timer a abstraktní java.util.TimerTask. Timer( ) – konstruktor. Timer( boolean isDaemon ) – konstruktor pro démona. void schedule( ... ) – naplánuje spuštění úkolu. void scheduleAtFixedRate( ... ) – spouštění s vyrovnáním skluzů. - tyto metody mají parametry: TimerTask task – naplánovaný úkol. Date firstTime – datum a čas prvního spuštění. long delay – zpoždění pro první spuštění. long period – perioda dalších spuštění. void cancel( ) – ukončí činnost objektu i naplánované úkoly. Uplynul-li již moment spuštění, spustí se ihned. PJV06
Časové spouštění úloh Abstraktní třída java.util.TimerTask má: protected TimerTask( ) – konstruktor. public void cancel( ) – ukončí naplánovaný úkol. public long scheduledExecutionTime( ) – čas posledního spuštění. public abstract void run( ) – definice činnosti. Timer t = new Timer( ); t.schedule( new TTA( ), 10000 ); t.scheduleAtFixedRate( new TTB( ), 5000, 500 ); class TTA extends TimerTask{ public void run( ) { System.out.println( “A” ); } } class TTB extends TimerTask{ public void run( ) { System.out.println( “B” ); } PJV06
Jak JVM spouští aplikaci Při spuštění java T aa bb ccc systémové vlákno, získá informace z cmd řádky, vytvoří ThreadGroup main a v ní vlákno main s prioritou 5 a pak ho odstartuje. Toto vlákno prochází postupně statickými inicializátory. Leč tam neodchycená výjimka způsobí ExceptionInInitializerError. V normálním případě vlákno main projde do metody T.main. class ... implements Runnable { public void run( ) { // Thread[ main, 5, main] try { // call all static initializers // call T.main( args ) } catch ( Throwable ex ) { ex.printStackTrace( ); } // tiskne parte finally { /* release resources */ } } PJV06