Multithreading vícevláknovost 1 aktivní objekty a jejich životní pravidla práce s vlákny prostředky pro paralelní programování vlákna podprocesy, sdílejí datové zdroje s konkurentností (souběžností) se setkáváme v API specifikaci - aplikace obsahuje běhová vlákna, kde každé vlákno určuje část programu, který může běžet souběžně s ostatními vlákny
Obsah přednášky 2 1. Úvod 2. Vlákno 3. Implementace vlákna 4. Vlákna uživatele a démoni 5. Priority vláken, plánování běhu vláken 6. Stavy vláken 7. Metoda jion() 8. Synchronizace vláken 9. Komunikace mezi vlákny 10. Deadlock uváznutí
1 Úvod 3 Concurrency (souběžnost) - normálně dostupná v primitivních operacích operačního systému Java má podporu pro vícevláknové zpracování zabudovanou v sobě (provides built-in multithreading) Multithreading zlepšuje výkonnost některých programů např. browserů a těch, kde je souběžnost implicitní Úlohy na prohledávání, vykreslování mohou využít svůj implicitní charakter souběžnosti
Procesy & vlákna 4 Proces Kód Data Status procesu Zdroje Abstraktní prostředí stroje (OS)
Procesy & vlákna 5 lightweight processes Vlákna Data Status vlákna Text programu Data Status procesu Zdroje Heavyweight process
2 Vlákno - thread 6 Vlákno je podobné sekvenčním programům Samotné vlákno není program; nemůže běžet samo o sobě; vlákno spíše běží uvnitř programu Vlákno je jeden sekvenční tok řízení v kontextu programu Hlavní využití vláken není v použití jednoho sekvenčního vlákna, ale ve využití více vláken běžících ve stejném čase, vykonávající různé úlohy daného programu.
Program - vlákno 7 Vlákno Program Program Dvě vlákna Vlákno spuštěné v kontextu programu Dvě vlákna současně spuštěná v kontextu jednoho programu
Vlákno 8 Někdy se vlákna proto nazývají lightweight process a procesy (heavyweight process) Vlákno je podobné procesu reálného světa má jeden sekvenční tok řízení Avšak vlákno se nazývá lightweight, protože běží uvnitř kontextu programu a využívá výhody, že zdroje jsou přiděleny programu a jeho prostředí. Vlákno si musí pro sebe získat některé zdroje uvnitř běžícího programu.
Vlákno 9 Vlákno má vlastní zásobník běhu (execution stack) a čítač programu (program counter) Kód prováděný ve vlákně funguje pouze tomto kontextu (kontextu programu). Program má vždy alespoň jedno vlákno, to jedno začíná od metody main(). (main vlákno) Když program vytvoří vlákno, je toto nové vlákno vytvořeno navíc k vláknu, které jej vytvořilo. Každé další vytvořené vlákno (mimo implicitní vlákno main) je představováno objektem třídy Thread, balíčku java.lang.thread.
import java.io.ioexception; public class TryThread extends Thread { private String firstname; // Store for first name private String secondname; // Store for second name private long awhile; // Delay in milliseconds Osnova 10 Jednoduchý příklad subclassing (dědičnost) // konstruktor public TryThread(String firstname, String secondname, long delay) { this.firstname = firstname; // Store the first name this.secondname = secondname; // Store the second name awhile = delay; // Store the delay setdaemon(true); // Thread is daemon public static void main(string[] args) { // Create three threads Thread first = new TryThread("Hopalong ", "Cassidy ", 200L); Thread second = new TryThread("Marilyn ", "Monroe ", 300L); Thread third = new TryThread("Slim ", "Pickens ", 500L); System.out.println("Press Enter when you have had enough...\n"); n"); first.start(); // Start the first thread second.start(); // Start the second thread third.start(); // Start the third thread
try { System.in.read read(); // Wait until Enter key pressed System.out out.println println(" ("Enter pressed......\n"); catch (IOException e) // Handle IO exception { System.out out.println println(e); // Output the exception System.out out.println println(" ("Ending main()"); return; Osnova 11 sleep() uvolní procesor, ale neuvolní objekty, které jsou aktuálním vláknem uzamknuty // Method where thread execution will start public void run() { try { while(true ) // Loop indefinitely... { System.out out.print print(firstname firstname); // Output first name sleep(awhile awhile); // Wait awhile msec. System.out out.print print(secondname + "\n");" // Output second name catch(interruptedexception e) // Handle thread interruption { System.out out.println println(firstname + secondname + e); // Output the exception
Press Enter when you have had enough... Osnova 12 Hopalong Marilyn Slim Cassidy Hopalong Monroe Marilyn Cassidy Hopalong Pickens Slim Cassidy Hopalong Monroe Marilyn Cassidy Hopalong Monroe Marilyn Pickens Slim Cassidy Hopalong Cassidy Hopalong Monroe Marilyn Enter pressed... Ending main()
Jednoduchý příklad implementace rozhraní Runnable 13 vlákno main načte počet opakování zadaný uživatelem hlavní program (vlákno main) vytvoří instanci theapp, která vykoná metodu doit() v metodě doit se vytvoří vlákno na které odkazuje proměnná t Thread first = new Thread( Jak, se,45l); Thread t = new Thread(this); v prvém případě - defaultní běhuschopný cíl ve druhém odkazuje na třídu, jejíž objekt implementoval metodu run()
class ThreadEx1 implements Runnable { private static int howmany; private void doit() { Thread t = new Thread( this ); t.start(); //*** startuje nove vytvorene vlakno for ( int i = 0; i < howmany; i++ ) System.out out.println println( "Prvni" vlakno" ); public void run() { //*** implementace metody run() for ( int i = 0; i < howmany; i++ ) System.out out.println println( "Druhe" vlakno" ); //*** t se zastavi, když se dostane na konec run() public static void main( String[ ] args ) throws Exception { if ( args.length < 1 ) throw new Exception( "\n\ninvoke ninvoke as: ThreadEx1 <how< many iterations>\n" n" ); howmany = new Integer( args[ 0 ] ).intvalue intvalue(); ThreadEx1 theapp = new ThreadEx1(); theapp.doit doit(); //*** main Thread stops when main exits Osnova 14 Výsledky aplikace: Prvni vlakno Druhe vlakno Prvni vlakno Druhe vlakno Prvni vlakno Druhe vlakno Na různých JVM - různé
Vysvětlení 15 běhuschopný cíl (runnable target) objekt, jehož třída implementuje metodu run() theapp je pouze instance vytvořená vláknem main this a theapp se odkazují na stejný objekt třídy ThreadEx1
Zastavení vláken 16 zastavení vláken se provede prostřednictvím instanční metody interrupted() metoda pouze signalizuje vláknu, že je přerušené vlákno pokračuje ve svéčinnosti dále. kontrola příznaku se provádí např. v metodě sleep(). Metoda sleep() kontroluje, zda bylo vlákno přerušeno a pokud ano, vyvolá výjimku typu InterruptedException.
import java.io.ioexception; Osnova 17 public class TryThread1 extends Thread { private String firstname; // Store for first name private String secondname; // Store for second name private long awhile; // Delay in milliseconds public TryThread1(String firstname, String secondname, long delay) { this.firstname = firstname; // Store the first name this.secondname = secondname; // Store the second name awhile = delay; // Store the delay //setdaemon(true setdaemon(true); // Thread is daemon public static void main(string[] args) { // Create three threads Thread first = new TryThread("Hopalong ", "Cassidy ", 200L); Thread second = new TryThread("Marilyn ", "Monroe ", 300L); Thread third = new TryThread("Slim ", "Pickens ", 500L); System.out.println("Press Enter when you have had enough...\n"); n"); first.start(); // Start the first thread second.start(); // Start the second thread third.start(); // Start the third thread
try { System.in.read read(); // Wait until Enter key pressed System.out out.println println(" ("Enter pressed......\n"); Osnova 18 // Interrupt the threads first.interrupt interrupt(); second.interrupt interrupt(); third.interrupt interrupt(); catch (IOException e) // Handle IO exception { System.out out.println println(e); // Output the exception System.out out.println println(" ("Ending main()"); return; // Method where thread execution will start
public void run() { try { while(true ) // Loop indefinitely... { System.out out.print print(firstname firstname); // Output first name sleep(awhile awhile); // Wait awhile msec. System.out out.print print(secondname + "\n");" // Output second name Osnova 19 catch(interruptedexception e) // Handle thread interruption { // Output the exception System.out out.println println(firstname + secondname + e); Hopalong Pickens Slim Monroe Marilyn Cassidy Hopalong Cassidy Hopalong Enter pressed... Hopalong Cassidy java.lang.interruptedexception: : sleep interrupted Ending main() Slim Pickens java.lang.interruptedexception: : sleep interrupted Marilyn Monroe java.lang.interruptedexception: : sleep interrupted
3 Implementace vlákna 20 1. Vytvoření podtřídy od třídy Thread a předeklarováním (zastíněním) metody run new Thread(this).start(); vlákna jsou vytvořena a odstartovaná uvnitř třídy 2. Implementací rozhraní Runnable new MyThread().start(); vytvoří a odstartuje vlákno třídy MyThread, které provede zastíněnou (předeklarovanou) metodu run třídy MyThread
Ukončení programu vícevláknové aplikace 21 V metodě run se deklarují tzv. životní pravidla daného vlákna. Pokud se vlákno dostane na konec metody run je jeho život ukončen Příklad nekonečné smyčky vlákno main je ukončeno, některá další vlákna pokračují
class RunForever implements Runnable { public static void main( String[ ] args ) { RunForever me = new RunForever(); // instance System.out out.println println( "main" thread about to exit!" ); new Thread( me ).start(); //*** main exits so main Thread stops public void run() { while ( true ) { System.out out.println println( "2nd Thread says 'hi hi!'..." ); try { Thread.sleep sleep( 10 ); // pause 10 milliseconds catch( InterruptedException e ) { System.err err.println println( e ); Osnova 22
main thread about to exit! Osnova 23
4 Vlákna uživatele a démoni User and Daemon Threads 24 Vlákna mohou být dvou typů: vlákna uživatele (doposud) démoni pokud skončí vlákno main, skončí také metody: setdeamon(boolean); isdaemon();
class NotRunForever implements Runnable { //*** vlakno main je uzivatelske ne vlakno demonu public static void main( String[ ] args ) { NotRunForever me = new NotRunForever(); Thread t1 = new Thread( me ); t1.setdaemon setdaemon( true ); //*** t1 je demon, ne uzivatelske vlakno t1.start(); try { Thread.sleep sleep( 50 ); //*** vlakno main catch( InterruptedException e ) { System.err err.println println( e ); System.out out.println println( "main" thread about to exit!" ); //*** main konci, t1 zastavuje, aplikace konci Osnova 25 public void run() { while ( true ) { System.out out.println println( "2nd Thread says 'hi hi!'..." ); try { Thread.sleep sleep( 10 ); // pauza 10 milisekund catch( InterruptedException e ) { System.err err.println println( e );
main thread about to exit! Osnova 26 Výpis aplikace
5 Priority vláken a plánování vláken Thread Priorities and Thread Scheduling 27 Java thread priority Priority v rozsahu 1-10 Timeslicing dávkováníčasu Každé vlákno dostane přidělený čas procesoru (nazvaný kvantum) Nechává běžet vlákna s nejvyšší prioritou Pravidelná alternace mezi vlákny dané priority víceúrovňová fronta s prioritami pro plánování běhu vláken (další obrázek)
Priorita vláken příklad plánování 28 Ready threads Thread.MAX_PRIORITY Priority 10 A B Priority 9 C Priority 8 Priority 7 D E F Priority 6 G Thread.NORM_PRIORITY Priority 5 H I Priority 4 Priority 3 Priority 2 J K Thread.MIN_PRIORITY Priority 1
class ThreadPriority implements Runnable { public static void main( String[ ] args ) { ThreadPriority tp = new ThreadPriority(); tp.testpriority testpriority(); public void run() { while ( true ) System.out out.println println( Thread.currentThread currentthread(). ().getname getname() ); private void testpriority() { Thread t1 = new Thread( this, "Normalni" priorita" ); t1.start(); //*** starts first Thread t2 = new Thread( this, "Max priorita" ); t2.setpriority setpriority( Thread.MAX_PRIORITY ); t2.start(); Osnova 29
Max priorita Max priorita Max priorita Max priorita Max priorita Max priorita Max priorita Max priorita Max priorita Max priorita Normalni priorita Max priorita Max priorita Max priorita Max priorita Max priorita Max priorita Max priorita Max priorita Max priorita Max priorita Max priorita Max priorita Osnova 30
6 Stavy vláken: Životní cyklus vlákna (životní pravidla) 31 Stavy vláken Born state stav vytvoření Vlákno bylo právě vytvořeno Ready state Vlákno vyvolalo metodu start Vlákno nyní může být vykonáváno (execute) Running state Vláknu je přidělen procesor a běží (is running) Dead state Vlákno dokončilo nebo ukončilo svoji činnost Vlákno ukončeno systémem
Životní cyklus vlákna stavový diagram start Ready 32 Born thread dispatch (assign a processor) quantum expiration yield I/O completes acquire lock interrupt Running notify notifyall enter synchronized statement timeout expires interrupt issue I/O request complete sleep wait Blocked Sleeping Waiting Když se vlákno ukončí (návrat z metody run), dosáhne stavu ukončení - Dead (konečný stav) sleep interval expires interrupt
Jak učinit vlákno neběžícím 33 Vyvolat metodu sleep() Vlákno vyvolá metodu wait() a čeká na splnění specifické podmínky a uvolňuje objekty, které jsou s aktuálním vláknem uzamknuty Vlákno je blokováno vstupně/výstupními operacemi Vyvolat metody yield() (vzdává se činnosti)
Zastavení stopnutí vlákna 34 Přirozeně, když se dostane v metodě run na konec svých životních pravidel příkazem System.exit(0); thread.interrupt();
Metoda join() 35 Čekání, až jiné vlákno zanikne; na jiné vlákno se použije metoda join(). Metoda join() zadrží aktuální vlákno tak dlouho, dokud určené vlákno nezanikne. Dá se proto částečně použít k synchronizaci vláken. Následující program dvě vlákna plus a minus (přičítá, odečítá 1) se střídají pomocí metody join() výsledný součet pro malé N nulový pro velké N různé výsledky
class SyncEx1 { public static void main( String[ ] args ) { if ( args.length < 1 ) { System.err err.println println( "java" SyncEx1 <loops< per thread>" ); System.exit( -1 ); int n = Integer.parseInt parseint( args[ 0 ] ); // iteraci na vlakno MyThreadE plus = new MyThreadE( +1, n ), // plus zvysi minus = new MyThreadE( -1, n ); // minus snizi plus.start(); minus.start(); // ceka az obe vlakna dokonci, pred tiskem vysledku try { plus.join join(); minus.join join(); //main ceka na ukonceni plus a minus catch( InterruptedException e ) { System.out out.println println( n + " iterations each: " + MyThreadE.shared ); class MyThreadE extends Thread { static int shared; //*** sdilene vsemi objekty MyThreadE private int n; private int t; MyThreadE( int type, int howmany ) { t = type; n = howmany; public void run() { for ( int i = 0; i < n; i++ ) if ( t > 0 ) shared = shared + 1; else shared = shared - 1; Osnova 36
Kritická sekce vzájemné vyloučení Mutual Exclusion 37 Kritická sekce část kódu, ke které musí mít vlákno výlučný (exklusivní) přístup. Operace: shared = shared + 1; se skládá ze součtu a přiřazení (2 atomické operace) Může být rozdělena na dvěčásti dostaneme chybné výsledky
Synchronizace 38 Nutná synchronizace Cíl synchronizace v případě, že několik vláken vyžaduje přístup k jedinému zdroji, mohl tento přístup v jediném okamžiku dostat jen jedno vlákno. Možnosti synchronizace: na úrovni metod synchronizovat celé metody na úrovni bloků pomocí synchronizace bloků. Klíčové slovo synchronized způsobí, že metody, bloky se budou chovat jako atomické - nedělitelné
Synchronizované metody 39 class MojeTrida { public synchronized void metoda1() { // kód metody... public synchronized void metoda2() { // kód metody public void metoda3() { //kód metody
class MyThreadS extends Thread { static int shared; //*** sdilene vsemi objekty MyThreadS private int n; private int t; private synchronized void plusorminus(){ if(t>0) shared = shared + 1; else shared = shared - 1; MyThreadS( int type, int howmany ) { t = type; n = howmany; public void run() { plusorminus(); //for ( int i = 0; i < n; i++ ) // if ( t > 0 ) // shared = shared + 1; // else // shared = shared - 1; Osnova 40 Zabezpečí pouze výlučný přístup exkluzivní přístup
Synchronizované bloky 41 Pružnější mechanismus než synchronizované metody, protože se váže pouze na blok a daný objekt, který je buď uzamknutý, nebo odemčený. Každá instance třídy java.lang.object, nebo libovolný potomek si udržuje zámek (někdy zvaný monitor ) jedná se u určitou formu detekce stavu s cílem poskytnout výhradní přístup k danému prostředku klíčové slovo synchronized je vždy implicitně nebo explicitně přidruženo k instanci typu Object
Synchronizované bloky 42 Předtím, než je vláknu dovoleno vstoupit do synchronizovaného bloku musí získat zámek objektu přidruženého k danému kódu. Pokud jedno vlákno zámek získá, jiné jej nezíská a jeho běh bude blokovaný do doby, než bude zámek uvolněn. Kromě zámku si ještě každý objekt udržuje seznam blokovaných vláken pokud vlákno nemůže získat zámek objektu, je automaticky uloženo do seznamu (fronty) a pak z něho vybráno.
class Bank { // Perform a transaction public void dotransaction(transaction transaction) { int balance = transaction.getaccount getaccount(). ().getbalance getbalance(); Osnova 43 switch(transaction transaction.gettransactiontype gettransactiontype()) { case Transaction.CREDIT: synchronized(transaction transaction.getaccount getaccount()) { // Get current balance int balance = transaction.getaccount getaccount(). ().getbalance getbalance(); // Credits require a lot of checks... try { Thread.sleep sleep(100); catch(interruptedexception e) { System.out out.println println(e); balance += transaction.getamount getamount(); // Increment the balance // Restore account balance transaction.getaccount getaccount(). ().setbalance setbalance(balance); break;
case Transaction.DEBIT: synchronized(transaction transaction.getaccount getaccount()) { // Get current balance int balance = transaction.getaccount getaccount(). ().getbalance getbalance(); Osnova 44 // Debits require even more checks... try { Thread.sleep sleep(150); catch(interruptedexception e) { System.out out.println println(e); balance -= transaction.getamount getamount(); // Decrement the balance // Restore account balance transaction.getaccount getaccount(). ().setbalance setbalance(balance); break; default: // We should never get here System.out out.println println("invalid transaction"); System.exit(1); // Restore the account balance transaction.getaccount getaccount(). ().setbalance setbalance(balance);
Komunikace mezi vlákny 45 Probrali jsme uzamčení metod nebo bloků kódu pomocí synchronizace Používáme příkaz while k testování stavu vlákna v časových intervalech daných voláním metody sleep() není nejlepšířešení Třída Object definuje metody wait(), notify() a notifyall() účinnějšířešení situace 1. Tyto metody dědí všechny podtřídy třídy Object 2. Mohou být volány jen zevnitř synchronizovaných metod nebo zevnitř synchronizovaného bloku kódu.
Komunikace mezi vlákny 46 Při vyvolání odjinud bude vyvolána výjimka IllegalMonitorStateException Základní myšlenkou metod wait() a notify() je poskytnout metodám nebo blokům kódu, které jsou synchronizovány konkrétním objektem, prostředek ke komunikaci. jedem blok může volat wait() k pozastavení svých operací, dokud nějaká jiná metoda nebo blok kódu synchronizovaný stejným objektem jej nějakým způsobem nezmění a nezavolá notify(), čímž naznačí dokončení změny.
Metoda wait() wait(long caslim) notify() notifyall() Popis Metoda pozastaví aktuální vlákno až do doby volání metody notify() nebo notifyall() pro objekt, ke kterému patří metoda wait(). Při zavolání této metody vlákno uvolní synchronizační zámek, který má na objektu, takže se může provádět jakákoli jiná metoda nebo blok kódu, které jsou synchronizovány stejným objektem. Tato verze pozastaví aktuální vlákno dokud neuplyne stanovený počet milisekund, nebo není dříve zaslaná zpráva notify() nebo notifyall() objektu, ke kterému patří metoda wait().. Metodou se znova spustí vlákno, které vyvolalo metodu wait() objektu, ke kterému patří metoda notify(). Pokud pro tento objekt volalo wait() více objektů, není žádná kontrola nad tím, kterému z těchto vláken bude notify() určeno lépe použít notifyall(). Touto metodou se znovu spustí všechna vlákna, která volala metodu wait() objektu, ke kterému patří metoda notifyall(). 47
Příklad komunikace vláken 48 Metoda main vytvoří N (8) vláken třídy MyThreadW Třída MyThreadW má statické pole znaků sdílených objektů Každé vlákno má id a char (identifikátor a znak) začínající od 0 A Start se provádí v obráceném pořadí od 7 H Výpis je opět uspořádán od A H
class SyncWaitEx { public static void main( String[ ] args ) { final int n = 8; MyThreadW[ ] mt = new MyThreadW[ n ]; char c = 'A'; //*** vytvari a inicializuje vlakna for ( int i = 0; i < n; i++ ) mt[ i ] = new MyThreadW( i, c++ ); //*** startuje vlakna v obracenem poradi for ( int i = n - 1; i >= 0; i--i ) mt[ i ].start(); class MyThreadW extends Thread { private int id; //*** identifikator private char c; //*** znak, ktery se zapise do sdileneho pole private static int turn = 0; private static final int n = 8; private static char queue[ ] = new char[ n ]; //** sdilene pole public MyThreadW( int i, char ch ) { id = i; c = ch; public void run() { while ( true ) writechar(); Osnova 49
//*** synchronizace zabezpecujici pouze jedno vlakno v danem case // zapisuje do fronty apod. Vlakna cekaji, kdy prijdou na radu. private synchronized void writechar() { //*** uvolneni lock a ceka az prijde na radu while ( turn!= id ) try { wait( 20 ); // 20 milisekund catch( InterruptedException e ) { queue[ turn++ ] = this.c; if ( turn == n ) { //*** je fronta plna? System.out out.println println( queue ); //** pokud ano, vytiskni ji turn = 0; // a nastav poradi turn turn] na nulu notifyall(); //*** vzbudit cekajici vlakna Osnova 50 ABCDEFGH ABCDEFGH ABCDEFGH ABCDEFGH ABCDEFGH ABCDEFGH ABCDEFGH ABCDEFGH ABCDEFGH
Producent / konzument 51 Producent vyrábí, konzument konzumuje CubbyHole vyrovnávací a synchronizační buffer Testovací třída
public class Producer extends Thread { private CubbyHole cubbyhole; private int number; Osnova 52 public Producer(CubbyHole c, int number) { cubbyhole = c; this.number = number; public void run() { for (int i = 0; i < 10; i++) { cubbyhole.put(.put(number number, i); try { sleep(( ((int int)( )(Math Math.random random() * 100)); catch (InterruptedException e) {
public class Consumer extends Thread { private CubbyHole cubbyhole; private int number; Osnova 53 public Consumer(CubbyHole CubbyHole c, int number) { cubbyhole = c; this.number = number; public void run() { int value = 0; for (int i = 0; i < 10; i++) { value = cubbyhole.get get(number number);
public class CubbyHole { private int contents; private boolean available = false; Osnova 54 public synchronized int get(int who) { while (available == false) { try { wait(); catch (InterruptedException e) { available = false; System.out out.format format(" ("Consumer %d got: %d%n", who, contents); notifyall(); return contents; public synchronized void put(int who, int value) { while (available == true) { try { wait(); catch (InterruptedException e) { contents = value; available = true; System.out out.format format(" ("Producer %d put: %d%n", who, contents); notifyall();
public class ProducerConsumerTest { public static void main(string args[]){ CubbyHole c = new CubbyHole(); Producer p1 = new Producer(c,1); Consumer c1 = new Consumer(c,1); Osnova 55 p1.start(); c1.start(); Producer 1 put: 0 Consumer 1 got: 0 Producer 1 put: 1 Consumer 1 got: 1 Producer 1 put: 2 Consumer 1 got: 2 Producer 1 put: 3 Consumer 1 got: 3 Producer 1 put: 4 Consumer 1 got: 4 Producer 1 put: 5 Consumer 1 got: 5 Producer 1 put: 6 Consumer 1 got: 6 Producer 1 put: 7 Consumer 1 got: 7 Producer 1 put: 8 Consumer 1 got: 8 Producer 1 put: 9 Consumer 1 got: 9
Deadlock - uváznutí 56 Uváznutí se týká vzájemné závislosti dvou vláken např. jedno vlákno provádí kód, který je synchronizovaný daným objektem (tenobjekt) pak má provést další metodu synchronizovanou jiným objektem (jinyobjekt) než k tomu dojde, druhé vlákno provede kód synchronizovaný jinyobjekt a potřebuje provést metodu obsahující kód synchronizovaný prvním objektem (tenobjekt) žádné vlákno nemá možnost pokračovat, obě uvázla
class DeadlockEx { public static void main( String[ ] args ) { new MyThread( "foo" foo" ).start(); new MyThread( "bar" ).start(); class MyThread extends Thread { public MyThread( String name ) { super( name ); public void run() { while ( true ) if ( getname(). ().equalsignorecase equalsignorecase( "foo" foo" ) ) foom(); else barm(); private synchronized void foom() { System.out out.println println( getname() + " vstup foom." ); while ( getname(). ().equalsignorecase equalsignorecase( "bar" ) ) try { wait(); // cekani na neurcito catch( InterruptedException e ) { barm(); // vyvola jinou synchronizacni metodu System.out out.println println( getname() + " vystup foom." ); notify(); // vzbudi jine vlakno, pokud nejake ceka Osnova 57
private synchronized void barm() { System.out out.println println( getname() + " vstup barm." ); while ( getname(). ().equalsignorecase equalsignorecase( "foo" foo" ) ) try { wait(); // cekani na neurcito catch( InterruptedException e ) { foom(); // vyvola jinou synchronozacni metodu System.out out.println println( getname() + " vystup barm." ); notify(); // vzbudi jine vlakno, pokud nejake ceka Osnova 58 foo vstup foom. bar vstup barm. foo vstup barm. bar vstup foom.
Uváznutí 59 Problémem uváznutí jsou vztahy mezi metodami wait(), notify() a notofyall(). Metoda wait() musí předcházet metodu notify(). Pokud je tomu naopak, vlákno nemůže být probuzeno. V uvedeném programu vlákno foo vykonává synchronizovanou nejdříve metodu foom() a poté synchronizovanou metodu barm(). Vlákno bar vykoná nejdříve synchronizovanou metodu barm() a potom synchronizovanou metodu foom(). Avšak každé vlákno čeká v metodě jména svého protějšku. Vlákno foočeká v metodě barm() a vlákno bar čeká v metodě foom().
Uváznutí 60 Vlákno foo získá zámek na barm a čeká. Vlákno foo může být probuzeno pouze vláknem bar, které vyvolá metodu notify() v metodě barm(). Vlákno foo nezíská zámek na barm() protože vlákno bar má aktuálně tento zámek. Dokud vlákno foo nezíská tento zámek, bude čekat.
Nejdůležitější konstruktory třídy Thread 61 Deklarace Thread(); Popis Nepojmenované vlákno s defóltním běhuschopným cílem. Thread(String s); Pojmenované vlákno s defóltním běhuschopným cílem. Thread(Runnable target); Nepojmenované vlákno se specifikovaným běhuschopným cílem. Thread(Runnable t, String s); Pojmenované vlákno se specifikovaným běhuschopným cílem.
Třídní metody třídy Thread 62 Deklarace Thread currentthread(); Popis Vrací odkaz na právě běžící vlákno (current). boolean interrupted(); Testuje, zda běžící vlákno (current) má příznak interrupted. void sleep(long n); Uspí běžící (current) vlákno na n milisekund.