Vlákna v Javě
Z MiS
(Rozdíly mezi verzemi)
(Přidána komunikace mezi vlákny, upravena struktura...) |
(Oprava kódu časovače, doplněn anonymní listener.) |
||
(Nejsou zobrazeny 2 mezilehlé verze od 1 uživatele.) | |||
Řádka 1: | Řádka 1: | ||
− | [[Category:VSE]][[Category:Informatika]][[Category:Java]] | + | [[Category:VSE]][[Category:Informatika]][[Category:Java]][[Category:Stránky s obrázky]][[Category:Programování]] |
== Výhody a nevýhody práce s vlákny == | == Výhody a nevýhody práce s vlákny == | ||
Řádka 26: | Řádka 26: | ||
run() | run() | ||
*Popisuje, co má vlákno vykonávat. | *Popisuje, co má vlákno vykonávat. | ||
− | <div class="Varovani> | + | <div class="Varovani">Metodu <code>run()</code> nikdy nevoláme přímo, spustí ji třída <tt>Thread</tt> sama při volání metody <code>start()</code>.</div> |
start() | start() | ||
* Vytvoří vlákno a spustí v něm kód metody <code>run()</code>. | * Vytvoří vlákno a spustí v něm kód metody <code>run()</code>. | ||
Řádka 98: | Řádka 98: | ||
* Přidáme posluchače (''listenery'', implementují rozhraní <tt>ActionListener</tt>). | * Přidáme posluchače (''listenery'', implementují rozhraní <tt>ActionListener</tt>). | ||
* <tt>Timer</tt> ve stanovených intervalech posílám všem posluchačům zprávu <tt>actionPerformed</tt>. | * <tt>Timer</tt> ve stanovených intervalech posílám všem posluchačům zprávu <tt>actionPerformed</tt>. | ||
+ | |||
+ | ; Vytvoření časovače | ||
+ | Timer ''nazevCasovace'' = new Timer(''delkaIntervaluMs'', ''listener''); | ||
+ | |||
+ | ; Spuštění časovače | ||
+ | ''nazevCasovace''.start(); | ||
+ | |||
+ | ; Příklady kódu | ||
<div class="Priklad"> | <div class="Priklad"> | ||
− | + | * Příprava posluchače | |
− | class Posluchac implements ActionListener | + | class Posluchac implements ActionListener { |
public void actionPerformed(ActionEvent e) { | public void actionPerformed(ActionEvent e) { | ||
... ''co se má pravidelně vykonat'' ... | ... ''co se má pravidelně vykonat'' ... | ||
} | } | ||
} | } | ||
− | + | * Spuštění časování | |
− | Timer t = new Timer(new Posluchac() | + | Timer t = new Timer(1000, new Posluchac()); |
+ | t.start(); | ||
+ | </div> | ||
+ | <div class="Priklad"> | ||
+ | * Můžeme samozřejmě také použít anonymní listener (od Javy 8 i s lambda notací) | ||
+ | Timer t = new Timer(1000, (ActionEvent e) -> { System.out.println("Tik!"); } ); | ||
t.start(); | t.start(); | ||
</div> | </div> | ||
+ | |||
Řádka 117: | Řádka 131: | ||
; Příklad — Pravidelný výpis stavu výpočtu | ; Příklad — Pravidelný výpis stavu výpočtu | ||
[[Image:komunikace-vlaken.png]] | [[Image:komunikace-vlaken.png]] | ||
+ | |||
== Další informace == | == Další informace == | ||
* [http://docs.oracle.com/javase/tutorial/essential/concurrency/threads.html Docs.Oracle.com > JavaSE > Tutorial > Essential > Concurrency > Threads] | * [http://docs.oracle.com/javase/tutorial/essential/concurrency/threads.html Docs.Oracle.com > JavaSE > Tutorial > Essential > Concurrency > Threads] |
Aktuální verze z 11. 6. 2018, 08:51
Obsah |
Výhody a nevýhody práce s vlákny
- Výhody
- Využití výkonu počítače (Za jakých okolností?)
- Oddělení nesouvisejících úkolů v aplikaci.
- Problémy
- Nutnost synchronizace + riziko uváznutí — deadlock.
- Výrazně obtížnější ladění programu!!!
- Kdy má smysl?
- Když provádím několik relativně nezávislých činností.
- Každá činnost může běžet v jiném jádře procesoru.
- Jeden kus kódu má běžet stejně rychle bez ohledu na délku zpracování jiného.
- Animace na pozadí.
- Dvě okna stejného programu.
Implementace v Javě
- Vlákno je vždy provázáno s instancí třídy Thread
- Metody třídy Thread
run()
- Popisuje, co má vlákno vykonávat.
Metodu
run()
nikdy nevoláme přímo, spustí ji třída Thread sama při volání metody start()
.start()
- Vytvoří vlákno a spustí v něm kód metody
run()
.
Thread.sleep(int time)
- Třídní metoda.
- Zastaví aktuální vlákno, odkud je zavolána, na stanovený počet milisekund.
- Můžeme volat kdekoli, i v programu, který vlákna nevyužívá!
stop() — nedoporučuje se!!!
- Pokud chceme vlákno ukončit z jiného vlákna, měli bychom připravit atribut boolean a hodnotu pravidelně testovat.
- Viz Komunikace mezi vlákny
Metoda
stop()
je zastaralá (depricated) a neměla by se používat! Vlákno by mělo skončit vždy samo ve vhodný okamžik na základě ukončení metody run()
.
Spuštění vlastního vlákna
- Postup
- Implementujeme v naší třídě (třeba Vlakno) rozhraní Runnable.
- Musíme doplnit metodu public void run().
- V metodě run() napíšeme kód, který má vlákno provádět.
- Vytvoříme instanci třídy Vlakno.
- Vytvoříme instanci třídy Thread.
- V konstruktoru předáme instanci třídy Vlakno.
- Zavoláme metodu start() třídy Thread.
- Popis chování vlákna
class Vlakno implements Runnable { ... public void run() { ... co se má dělat ve vlákně ... } ... }
- Spuštění vlákna
Vlakno v = new Vlakno(); Thread t = new Thread(v);
Z více vláken nelze používat
- Greenfoot
- Není Thread-safe!!!!
- Nevolejte z jiného vlákna funkce Greenfootu!
- ArrayList, kolekce
- Lze zasynchronizovat.
- Scanner
Modifikátor synchronized
- Pro metodu způsobí, že metoda bude prováděna jako celek.
- Provádění metody nebude přerušeno přepnutím do jiného vlákna.
- Pro blok
- Používá zámek zadaného objektu.
- Může se pak dělat jen jeden z bloků
synchronized {...}
Synchronizace kolekce
List<Prvek> data = Collections.synchronizedList(new ArrayList<Prvek>());
- Ale je třeba dát iteraci do synchronizovaného bloku:
synchronized(data) { Iterator i = data.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); }
Časování — třída Timer
- V Javě více variant, například:
javax.swing.Timer
. - S pravidelnými časovými intervaly vyvolává zadaný kód.
- Běží ve vlastním vlákně.
- Přidáme posluchače (listenery, implementují rozhraní ActionListener).
- Timer ve stanovených intervalech posílám všem posluchačům zprávu actionPerformed.
- Vytvoření časovače
Timer nazevCasovace = new Timer(delkaIntervaluMs, listener);
- Spuštění časovače
nazevCasovace.start();
- Příklady kódu
- Příprava posluchače
class Posluchac implements ActionListener { public void actionPerformed(ActionEvent e) { ... co se má pravidelně vykonat ... } }
- Spuštění časování
Timer t = new Timer(1000, new Posluchac()); t.start();
- Můžeme samozřejmě také použít anonymní listener (od Javy 8 i s lambda notací)
Timer t = new Timer(1000, (ActionEvent e) -> { System.out.println("Tik!"); } ); t.start();
Komunikace mezi vlákny
- Měli bychom se snažit minimalizovat počet proměnných, které je potřeba používat z více vláken.
- Pokud používáme složitější datové struktury (kolekce,...), je třeba zajistit synchronizaci.
- Příklad — Pravidelný výpis stavu výpočtu