Programování

Úvod do vláken Java

Tento článek, jeden z vůbec prvních publikovaných v JavaWorld, popisuje, jak jsou vlákna implementována v programovacím jazyce Java, počínaje obecným přehledem vláken.

Jednoduše řečeno, a vlákno je cesta k provedení programu. Většina dnes napsaných programů běží jako jeden podproces, což způsobuje problémy, když je třeba provést více událostí nebo akcí najednou. Řekněme například, že program není schopen kreslit obrázky při čtení úhozů. Program musí věnovat plnou pozornost vstupu na klávesnici bez schopnosti zvládnout více než jednu událost najednou. Ideálním řešením tohoto problému je plynulé spuštění dvou nebo více částí programu současně. Vlákna nám to umožňují.

Učení o vláknech Java

Tento článek je součástí archivu technického obsahu JavaWorld. V následujícím textu se dozvíte více o vláknech Java a souběžnosti:

Porozumění podprocesům Java (Java 101 série, 2002):

  • Část 1: Představujeme vlákna a spouštěcí tabulky
  • Část 2: Synchronizace vláken
  • Část 3: Plánování vláken a čekání / upozornění
  • Část 4: Skupiny vláken a volatilita

Související články

  • Hypervláknová Java: Použití rozhraní Java Concurrency API (2006)
  • Lepší monitory pro vícevláknové programy (2007)
  • Porozumění herectví souběžnosti, část 1 (2009)
  • Detekce závěsů a manipulace s nimi (2011)

Zkontrolujte také JavaWorld mapa stránek a vyhledávač.

Vícevláknové aplikace dodávají svůj silný výkon spuštěním více vláken současně v rámci jednoho programu. Z logického hlediska vícevláknový proces znamená, že lze spustit více řádků jednoho programu najednou, ale není to totéž jako dvakrát spustit program a říci, že existuje více řádků programu prováděného současně čas. V tomto případě operační systém považuje programy za dva samostatné a odlišné procesy. V systému Unix rozvětvení procesu vytvoří podřízený proces s jiným adresním prostorem pro kód i data. Nicméně, Vidlička() vytváří spoustu režie pro operační systém, což z něj činí velmi náročnou operaci CPU. Spuštěním podprocesu místo toho se vytvoří efektivní cesta provedení, zatímco stále sdílíte původní datovou oblast z nadřazeného objektu. Myšlenka sdílení datové oblasti je velmi přínosná, ale přináší některé oblasti zájmu, o kterých budeme diskutovat později.

Vytváření vláken

Tvůrci Javy laskavě navrhli dva způsoby vytváření vláken: implementaci rozhraní a rozšíření třídy. Rozšíření třídy je způsob, jakým Java dědí metody a proměnné z nadřazené třídy. V tomto případě lze rozšířit nebo zdědit pouze z jedné nadřazené třídy. Toto omezení v rámci Javy lze překonat implementací rozhraní, což je nejběžnější způsob vytváření vláken. (Všimněte si, že dědění pouze umožňuje třídu spustit jako vlákno. Je to na třídě Start() provedení atd.)

Rozhraní poskytují programátorům způsob, jak položit základy třídy. Používají se k návrhu požadavků na sadu tříd k implementaci. Rozhraní nastavuje vše a veškerou práci dělá třída nebo třídy, které rozhraní implementují. Různá sada tříd, které implementují rozhraní, musí dodržovat stejná pravidla.

Mezi třídou a rozhraním existuje několik rozdílů. Nejprve může rozhraní obsahovat pouze abstraktní metody a / nebo statické konečné proměnné (konstanty). Na druhou stranu třídy mohou implementovat metody a obsahovat proměnné, které nejsou konstantami. Zadruhé, rozhraní nemůže implementovat žádné metody. Třída, která implementuje rozhraní, musí implementovat všechny metody definované v tomto rozhraní. Rozhraní má schopnost rozšířit se z jiných rozhraní a (na rozdíl od tříd) se může rozšířit z více rozhraní. Kromě toho nelze vytvořit instanci rozhraní s novým operátorem; například, Runnable a = new Runnable (); není povoleno.

První metodou vytvoření vlákna je jednoduché rozšíření z Vlákno třída. Udělejte to pouze v případě, že třída, kterou potřebujete spustit jako vlákno, nemusí být nikdy rozšířena z jiné třídy. The Vlákno třída je definována v balíčku java.lang, který je třeba importovat, aby si naše třídy byly vědomy jeho definice.

import java.lang. *; public class Counter extends Thread {public void run () {....}}

Výše uvedený příklad vytvoří novou třídu Čelit který rozšiřuje Vlákno třídy a přepíše Thread.run () metoda vlastní implementace. The běh() metoda je místo, kde veškerá práce Čelit vlákno třídy je hotové. Stejnou třídu lze vytvořit implementací Runnable:

import java.lang. *; public class Counter implementuje Runnable {Thread T; public void run () {....}}

Tady, abstrakt běh() metoda je definována v rozhraní Runnable a je implementována. Všimněte si, že máme instanci Vlákno třída jako proměnná Čelit třída. Jediný rozdíl mezi těmito dvěma metodami spočívá v tom, že implementací Runnable existuje větší flexibilita při vytváření třídy Čelit. Ve výše uvedeném příkladu stále existuje příležitost rozšířit Čelit třídy, pokud je potřeba. Většina vytvořených tříd, které je třeba spustit jako vlákno, bude implementovat Runnable, protože pravděpodobně rozšiřují některé další funkce z jiné třídy.

Nemyslete si, že rozhraní Runnable dělá při provádění vlákna nějakou skutečnou práci. Je to pouze třída vytvořená za účelem získání představy o designu Vlákno třída. Ve skutečnosti je velmi malý a obsahuje pouze jednu abstraktní metodu. Zde je definice rozhraní Runnable přímo ze zdroje Java:

balíček java.lang; veřejné rozhraní Runnable {public abstract void run (); }

To je vše, co je k rozhraní Runnable. Rozhraní poskytuje pouze návrh, na kterém by měly být třídy implementovány. V případě Runnable rozhraní vynutí definici pouze běh() metoda. Proto se většina práce provádí v Vlákno třída. Bližší pohled na část v definici Vlákno třída dá představu o tom, co se ve skutečnosti děje:

public class Thread implements Runnable {... public void run () {if (target! = null) {target.run (); }} ...}

Z výše uvedeného fragmentu kódu je zřejmé, že třída Thread také implementuje rozhraní Runnable. Vlákno.běh() zkontroluje, zda se cílová třída (třída, která bude spuštěna jako vlákno) nerovná null, a poté provede běh() metoda cíle. Když k tomu dojde, běh() metoda cíle bude spuštěna jako vlastní vlákno.

Spuštění a zastavení

Jelikož jsou nyní zřejmé různé způsoby, jak vytvořit instanci vlákna, probereme implementaci vláken počínaje způsoby, jak je spustit a zastavit pomocí malého appletu obsahujícího vlákno pro ilustraci mechaniky:

Příklad CounterThread a zdrojový kód

Výše uvedený applet začne počítat od 0 se zobrazením jeho výstupu na obrazovku i konzolu. Krátký pohled může vyvolat dojem, že program začne počítat a zobrazovat každé číslo, ale není tomu tak. Podrobnější zkoumání provádění tohoto appletu odhalí jeho skutečnou identitu.

V tomto případě CounterThread třída byla nucena implementovat Runnable, protože rozšířila třídu Applet. Jako ve všech appletech, i init () metoda se provede jako první. v init (), proměnná Count je inicializována na nulu a nová instance Vlákno třída je vytvořena. Projížděním tento do Vlákno konstruktor, nové vlákno bude vědět, který objekt má být spuštěn. V tomto případě tento je odkaz na CounterThread. Po vytvoření vlákna je třeba jej spustit. Volání na Start() zavolá cíl běh() metoda, což je CounterThread.běh(). Volání na Start() se okamžitě vrátí a podproces se začne vykonávat současně. Všimněte si, že běh() metoda je nekonečná smyčka. Je to nekonečné, protože kdysi běh() metoda se ukončí, vlákno se zastaví. The běh() metoda zvýší proměnnou Count, sleep na 10 milisekund a odešle požadavek na obnovení zobrazení appletu.

Všimněte si, že je důležité spát někde ve vlákně. Pokud ne, vlákno spotřebuje veškerý čas CPU na proces a neumožní provedení dalších metod, jako jsou vlákna. Dalším způsobem, jak zastavit provádění vlákna, je zavolat stop() metoda. V tomto příkladu se vlákno zastaví, když stisknete myš, když je kurzor v appletu. V závislosti na rychlosti počítače, na kterém applet běží, se nezobrazí každé číslo, protože přírůstek se provádí nezávisle na malbě appletu. Aplet nelze aktualizovat při každém požadavku, takže operační systém bude frontu požadavků a následné žádosti o aktualizaci budou uspokojeny s jednou aktualizací. Zatímco se aktualizace zařazují do fronty, počet se stále zvyšuje, ale nezobrazuje se.

Pozastavení a obnovení

Jakmile je vlákno zastaveno, nelze jej znovu spustit pomocí Start() velení, protože stop() ukončí provádění vlákna. Místo toho můžete pozastavit provádění vlákna pomocí spát() metoda. Vlákno bude po určitou dobu spát a poté bude spuštěno, jakmile bude dosaženo časového limitu. To však není ideální, pokud je nutné vlákno spustit, když dojde k určité události. V tomto případě pozastavit() metoda umožňuje vláknu dočasně zastavit provádění a životopis() metoda umožňuje znovu spustit pozastavené vlákno. Následující applet ukazuje výše uvedený příklad upravený tak, aby pozastavil a obnovil applet.

veřejná třída CounterThread2 rozšiřuje Applet implementuje Runnable {Thread t; int Count; boolean pozastaveno; public boolean mouseDown (Událost e, int x, int y) {if (pozastaveno) t.resume (); else t.suspend (); pozastaveno =! pozastaveno; návrat true; } ...}

Příklad CounterThread2 a zdrojový kód

Chcete-li sledovat aktuální stav appletu, logická proměnná pozastaveno se používá. Rozlišování různých stavů appletu je důležité, protože některé metody vyvolají výjimky, pokud jsou volány v nesprávném stavu. Například pokud byl applet spuštěn a zastaven, spuštěním Start() metoda hodí IllegalThreadStateException výjimka.