Programování

Modern threading: a Java concurrency primer

Hodně z toho, co je třeba se dozvědět o programování pomocí podprocesů Java, se během vývoje platformy Java dramaticky nezměnilo, ale změnilo se to postupně. V tomto základu vláken Java Cameron Laird zasáhne některé z vysokých (a nízkých) bodů vláken jako techniku ​​souběžného programování. Získejte přehled toho, co je na vícevláknovém programování trvale náročné a zjistěte, jak se platforma Java vyvinula, aby splnila některé z výzev.

Souběžnost je jednou z největších starostí nováčků v programování v Javě, ale není důvod, aby vás to nechalo odradit. K dispozici je nejen vynikající dokumentace (v tomto článku prozkoumáme několik zdrojů), ale s vývojem platformy Java se práce s vlákny Java usnadňuje. Abyste se naučili, jak dělat vícevláknové programování v Javě 6 a 7, opravdu potřebujete jen několik stavebních bloků. Začneme těmito:

  • Jednoduchý podprocesový program
  • Vlákání je hlavně o rychlosti, že?
  • Výzvy souběžnosti Java
  • Kdy použít Runnable
  • Když se dobrá vlákna pokazí
  • Co je nového v prostředí Java 6 a 7
  • Co bude dál s vlákny Java

Tento článek je průzkumem začátečnických technik Java, včetně odkazů na některé z nejčastěji čtených úvodních článků JavaWorld o programování s více vlákny. Spusťte motory a postupujte podle výše uvedených odkazů, pokud jste připraveni začít se dnes učit o vláknech Java.

Jednoduchý podprocesový program

Zvažte následující zdroj Java.

Výpis 1. FirstThreadingExample

třída FirstThreadingExample {public static void main (String [] args) {// Druhý argument je zpoždění mezi // po sobě jdoucími výstupy. Zpoždění je // měřeno v milisekundách. „10“, například //, znamená, „tisknout řádek každou // setinu sekundy“. ExampleThread mt = nový ExampleThread ("A", 31); ExampleThread mt2 = nový ExampleThread ("B", 25); ExampleThread mt3 = nový ExampleThread ("C", 10); mt.start (); mt2.start (); mt3.start (); }} třída ExampleThread rozšiřuje Thread {private int delay; public ExampleThread (String label, int d) {// Dejte tomuto konkrétnímu vláknu // název: "vlákno 'LABEL'". super ("vlákno" "+ štítek +" ""); zpoždění = d; } public void run () {for (int count = 1, row = 1; row <20; row ++, count ++) {try {System.out.format ("Line #% d from% s \ n", count, getName ()); Thread.currentThread (). Sleep (delay); } catch (InterruptedException ie) {// To by bylo překvapení. }}}}

Nyní zkompilujte a spusťte tento zdroj jako jakoukoli jinou aplikaci příkazového řádku Java. Uvidíte výstup, který vypadá asi takto:

Výpis 2. Výstup podprocesového programu

Řádek č. 1 ze závitu „A“ Řádek č. 1 ze závitu „C“ Řádek č. 1 ze závitu „B“ Řádek č. 2 ze závitu „C“ Řádek č. 3 ze závitu „C“ Řádek č. 2 ze závitu „B“ Řádek č. 4 ze závitu „C“ ... Řádek č. 17 ze závitu „B“ Řádek č. 14 ze závitu „A“ Řádek č. 18 ze závitu „B“ Řádek č. 15 ze závitu „A“ Řádek č. 19 ze závitu „B“ Řádek # 16 z vlákna 'A' Řádek # 17 z vlákna 'A' Řádek # 18 z vlákna 'A' Řádek # 19 ze vlákna 'A'

To je vše - jste Java Vlákno programátor!

No, dobře, možná ne tak rychle. Jakkoli malý je program v seznamu 1, obsahuje některé jemnosti, které si zaslouží naši pozornost.

Vlákna a neurčitost

Typický cyklus učení s programováním sestává ze čtyř fází: (1) Studium nového konceptu; (2) provést ukázkový program; (3) porovnat výstup s očekáváním; a (4) iterovat, dokud se dva neshodují. Všimněte si však, že jsem dříve řekl výstup pro FirstThreadingExample bude vypadat „něco jako“ Výpis 2. Takže to znamená, že váš výstup se může lišit od mého, řádek po řádku. Co je že o?

V nejjednodušších programech Java existuje záruka pořadí provádění: první řádek hlavní() budou provedeny první, pak další a tak dále, s příslušným trasováním dovnitř a ven z dalších metod. Vlákno oslabuje tuto záruku.

Threading přináší do programování Java novou sílu; výsledků můžete dosáhnout pomocí vláken, která byste bez nich nezvládli. Ale tato síla přichází za cenu rozhodnost. V nejjednodušších programech Java existuje záruka pořadí provádění: první řádek hlavní() budou provedeny první, pak další a tak dále, s vhodným trasováním dovnitř a ven z dalších metod. Vlákno oslabuje tuto záruku. Ve vícevláknovém programu „Řádek # 17 z vlákna B„se může na obrazovce objevit před nebo po“Řádek # 14 z vlákna A„“ a pořadí se může lišit při postupném spouštění stejného programu, a to i na stejném počítači.

Neurčitost může být neznámá, ale nemusí to být rušivé. Exekuční příkaz v rámci vlákno zůstává předvídatelné a s neurčitostí jsou spojeny také výhody. Něco podobného jste možná zažili při práci s grafickými uživatelskými rozhraními (GUI). Příkladem jsou posluchače událostí ve Swingu nebo obslužné rutiny událostí v HTML.

Zatímco celá diskuse o synchronizaci vláken je mimo rozsah tohoto úvodu, je snadné vysvětlit základy.

Zvažte například mechaniku toho, jak HTML určuje ... onclick = "myFunction ();" ... k určení akce, která se stane po kliknutí uživatele. Tento známý případ neurčitosti ilustruje některé z jeho výhod. V tomto případě, myFunction () není proveden v určitém čase s ohledem na ostatní prvky zdrojového kódu, ale ve vztahu k akci koncového uživatele. Neurčitost tedy není jen slabostí systému; je to také obohacení modelu provedení, který dává programátorovi nové příležitosti k určení posloupnosti a závislosti.

Zpoždění provádění a podtřídy podprocesů

Můžete se poučit z FirstThreadingExample vlastními experimenty. Zkuste přidat nebo odebrat ExampleThreads - to znamená vyvolání konstruktoru jako ... nový ExampleThread (štítek, zpoždění); - a vrtání s zpožděnís. Základní myšlenkou je, že program začíná tři samostatně Vláknos, které pak běží nezávisle až do dokončení. Aby jejich provedení bylo poučnější, každý z nich mírně zpozdí mezi po sobě jdoucími řádky, které zapisuje na výstup; toto dává ostatním vláknům šanci psát jejich výstup.

Všimněte si, že Vláknoprogramování na bázi obecně nevyžaduje manipulaci s Přerušená výjimka. Ten, který je zobrazen v FirstThreadingExample má co do činění s spát(), spíše než přímý vztah k Vlákno. Většina Vláknozdroj založený na nezahrnuje a spát(); účel spát() zde je jednoduchým způsobem modelovat chování dlouhodobých metod nalezených „ve volné přírodě“.

Něco jiného si v seznamu 1 všimněte Vlákno je abstraktní třída, určená k zařazení do podtřídy. Je to výchozí běh() metoda nedělá nic, takže musí být přepsána v definici podtřídy, aby bylo dosaženo čehokoli užitečného.

Jedná se o rychlost, že?

Nyní tedy můžete vidět trochu toho, co dělá programování pomocí vláken složitým. Ale hlavním bodem přetrvávání všech těchto obtíží není získat rychlost.

Vícevláknové programy neobecně se dokončují rychleji než jednovláknové - ve skutečnosti mohou být v patologických případech výrazně pomalejší. Základní přidaná hodnota vícevláknových programů je citlivost. Když je JVM k dispozici více jader zpracování, nebo když program stráví významný čas čekáním na více externích zdrojů, jako jsou síťové odpovědi, pak může vícevláknové zpracování pomoci programu dokončit rychleji.

Přemýšlejte o aplikaci s grafickým uživatelským rozhraním: pokud stále reaguje na body koncových uživatelů a kliká při hledání „na pozadí“ odpovídajícího otisku prstu nebo při přepočtu kalendáře pro tenisový turnaj příštího roku, pak byla vytvořena s ohledem na souběžnost. Typická souběžná aplikační architektura staví rozpoznávání a reakci na akce uživatelů do vlákna odděleného od výpočetního vlákna přiřazeného ke zpracování velkého back-endového zatížení. (Další ilustrace těchto principů najdete v části „Swing threading and the event-dispatch thread“.)

Ve vašem vlastním programování je tedy nejpravděpodobnější zvážit použití Vláknos za jedné z těchto okolností:

  1. Existující aplikace má správnou funkčnost, ale občas nereaguje. Tyto „bloky“ mají často co do činění s externími prostředky mimo vaši kontrolu: časově náročné databázové dotazy, komplikované výpočty, přehrávání multimédií nebo síťové odpovědi s nekontrolovatelnou latencí.
  2. Výpočtově náročná aplikace by mohla lépe využívat vícejádrové hostitele. To by mohl být případ někoho, kdo vykresluje složitou grafiku nebo simuluje zapojený vědecký model.
  3. Vlákno přirozeně vyjadřuje požadovaný programovací model aplikace. Předpokládejme například, že jste modelovali chování řidičů dopravní špičky nebo včel v úlu. Implementovat každého řidiče nebo včelu jako Vláknosouvisející objekt může být vhodný z hlediska programování, kromě jakýchkoli úvah o rychlosti nebo odezvě.

Výzvy souběžnosti Java

Zkušený programátor Ned Batchelder nedávno vtipkoval

Někteří lidé, když se setkají s problémem, si pomyslí: „Vím, použiji vlákna“, a pak dva, které mají erpoblesmy.

To je vtipné, protože tak dobře modeluje problém souběžnosti. Jak jsem již zmínil, programy s více vlákny pravděpodobně poskytnou různé výsledky, pokud jde o přesnou sekvenci nebo načasování provedení vlákna. To je znepokojující pro programátory, kteří jsou vyškoleni v přemýšlení o reprodukovatelných výsledcích, přísné rozhodnosti a invariantní posloupnosti.

Zhoršuje se to. Různá vlákna mohou nejen produkovat výsledky v různých objednávkách, ale mohou tvrdit na podstatnějších úrovních výsledků. Pro nováčka je snadné multithreading zavřít() popisovač souboru v jednom Vlákno před jiným Vlákno dokončil vše, co k psaní potřebuje.

Testování souběžných programů

Před deseti lety na serveru JavaWorld Dave Dyer poznamenal, že jazyk Java má jednu vlastnost tak „všudypřítomně nesprávně použitou“, že ji označil za závažnou konstrukční chybu. Tato funkce byla multithreading.

Dyerův komentář zdůrazňuje výzvu testování vícevláknových programů. Když už nemůžete snadno určit výstup programu, pokud jde o definitivní posloupnost znaků, bude to mít dopad na to, jak efektivně můžete otestovat váš vláknový kód.

Správný výchozí bod k řešení vnitřních potíží souběžného programování uvedl Heinz Kabutz ve svém zpravodaji Java Specialist: uznejte, že souběžnost je téma, kterému byste měli rozumět, a systematicky ji prostudujte. K dispozici jsou samozřejmě nástroje, jako jsou techniky diagramů a formální jazyky, které vám pomohou. Prvním krokem je ale zostřit svou intuici cvičením s jednoduchými programy, jako je FirstThreadingExample v Seznamu 1. Dále se naučte co nejvíce o základech vláken, jako jsou tyto:

  • Synchronizace a neměnné objekty
  • Plánování vláken a čekání / upozornění
  • Podmínky závodu a zablokování
  • Monitory vláken pro exkluzivní přístup, podmínky a tvrzení
  • Osvědčené postupy JUnit - testování vícevláknového kódu

Kdy použít Runnable

Objektová orientace v Javě definuje jednotlivě zděděné třídy, což má důsledky pro kódování více vláken. K tomuto bodu jsem popsal pouze použití pro Vlákno který byl založen na podtřídách s přepsaným běh(). V návrhu objektu, který již zahrnoval dědičnost, by to jednoduše nefungovalo. Nemůžete současně dědit z RenderedObject nebo Výrobní linka nebo MessageQueue vedle Vlákno!

Toto omezení ovlivňuje mnoho oblastí Javy, nejen multithreading. Naštěstí existuje klasické řešení problému v podobě Spustitelný rozhraní. Jak vysvětlil Jeff Friesen ve svém úvodu k vláknům z roku 2002, Spustitelný Rozhraní je určeno pro situace, kdy podtřídy Vlákno není možné:

The Spustitelný rozhraní deklaruje jeden podpis metody: void run ();. Ten podpis je totožný s Vláknoje běh() podpis metody a slouží jako podprocesový záznam provádění. Protože Spustitelný je rozhraní, kterákoli třída může implementovat toto rozhraní připojením nářadí klauzule do záhlaví třídy a poskytnutím vhodné běh() metoda. V době provádění může programový kód vytvořit objekt, nebo spustitelný, z této třídy a předat odkaz na spustitelný soubor příslušnému Vlákno konstruktor.

Takže pro ty třídy, které se nemohou rozšířit Vlákno, musíte vytvořit spustitelný soubor, abyste mohli využívat výhody multithreadingu. Sémanticky, pokud děláte programování na systémové úrovni a vaše třída je ve vztahu is Vlákno, pak byste měli podtřídu přímo z Vlákno. Ale většina použití vícevláknového zpracování na úrovni aplikace závisí na kompozici, a tak definuje a Spustitelný kompatibilní s třídním diagramem aplikace. Naštěstí kódování pomocí kódu vyžaduje pouze jeden nebo dva řádky Spustitelný rozhraní, jak je uvedeno v seznamu 3 níže.

$config[zx-auto] not found$config[zx-overlay] not found