Programovanie

Úvod do vlákien Java

Tento článok, jeden z prvých, ktorý publikoval JavaWorld, popisuje spôsob implementácie vlákien v programovacom jazyku Java, počnúc všeobecným prehľadom vlákien.

Jednoducho povedané, a závit je cesta vykonania programu. Väčšina dnes napísaných programov beží ako jedno vlákno, čo spôsobuje problémy, keď sa musí vyskytnúť viac udalostí alebo akcií súčasne. Povedzme napríklad, že program nie je schopný kresliť obrázky počas čítania klávesových skratiek. Program musí venovať plnú pozornosť vstupu klávesnice, ktorý neumožňuje zvládnuť viac ako jednu udalosť súčasne. Ideálnym riešením tohto problému je plynulé vykonávanie dvoch alebo viacerých častí programu súčasne. Vlákna nám to umožňujú.

Dozviete sa viac o vláknach Java

Tento článok je súčasťou archívu technického obsahu JavaWorld. Ďalej sa dozviete viac o vláknach Java a súbežnosti:

Pochopenie vlákien Java (Java 101 séria, 2002):

  • Časť 1: Predstavujeme vlákna a spustiteľné súbory
  • Časť 2: Synchronizácia vlákna
  • Časť 3: Plánovanie vlákna a čakanie / upozornenie
  • Časť 4: Skupiny vlákien a volatilita

Súvisiace články

  • Hypervláknová Java: Používanie rozhrania Java Concurrency API (2006)
  • Lepšie monitory pre viacvláknové programy (2007)
  • Pochopenie súbehu hercov, 1. časť (2009)
  • Detekcia a manipulácia so zavesenými vláknami (2011)

Skontrolujte tiež JavaWorld mapa stránok a vyhľadávač.

Viacvláknové aplikácie poskytujú svoju moc tým, že v jednom programe prevádzkujú viac vlákien súčasne. Z logického hľadiska viacvláknové spracovanie znamená, že je možné vykonať naraz viac riadkov jedného programu, nie je to však to isté ako spustiť program dvakrát a povedať, že existuje viac riadkov programu, ktorý sa vykonáva naraz čas. V takom prípade operačný systém považuje programy za dva samostatné a odlišné procesy. V systéme Unix vytvára vidlicový proces podradený proces s iným adresným priestorom pre kód aj údaje. Avšak vidlička() vytvára veľa réžie pre operačný systém, čo z neho robí operáciu veľmi náročnú na procesor. Ak namiesto toho spustíte vlákno, vytvorí sa efektívna cesta vykonávania, zatiaľ čo stále zdieľate pôvodnú dátovú oblasť od rodiča. Myšlienka zdieľania dátovej oblasti je veľmi prospešná, ale prináša určité oblasti znepokojenia, o ktorých si ešte povieme.

Vytváranie vlákien

Tvorcovia Java navrhli laskavo dva spôsoby vytvárania vlákien: implementáciu rozhrania a rozšírenie triedy. Rozšírenie triedy je spôsob, akým Java dedí metódy a premenné z nadradenej triedy. V takom prípade je možné rozšíriť alebo dediť iba z jednej nadradenej triedy. Toto obmedzenie v rámci Java je možné prekonať implementáciou rozhraní, čo je najbežnejší spôsob vytvárania vlákien. (Upozorňujeme, že akt dedenia iba umožňuje triede spustiť sa ako vlákno. Je to na triede.) štart () exekúcia atď.)

Rozhrania poskytujú programátorom spôsob, ako položiť základy triedy. Používajú sa na návrh požiadaviek na implementáciu súboru tried. Rozhranie nastaví všetko a všetku prácu urobí trieda alebo triedy, ktoré rozhranie implementujú. Rôzna sada tried, ktoré implementujú rozhranie, sa musia riadiť rovnakými pravidlami.

Medzi triedou a rozhraním je niekoľko rozdielov. Po prvé, rozhranie môže obsahovať iba abstraktné metódy a / alebo statické konečné premenné (konštanty). Triedy môžu na druhej strane implementovať metódy a obsahovať premenné, ktoré nie sú konštantami. Po druhé, rozhranie nemôže implementovať žiadne metódy. Trieda, ktorá implementuje rozhranie, musí implementovať všetky metódy definované v tomto rozhraní. Rozhranie má schopnosť rozšírenia z iných rozhraní a (na rozdiel od tried) sa môže rozšíriť z viacerých rozhraní. Ďalej nie je možné vytvoriť inštanciu rozhrania s novým operátorom; napríklad, Spustiteľný súbor a = nový Spustiteľný súbor (); nie je povolené.

Prvou metódou vytvorenia vlákna je jednoduché rozšírenie z Závit trieda. Urobte to iba v prípade, že triedu, ktorú potrebujete spustenú ako vlákno, nikdy nemusíte rozširovať z inej triedy. The Závit trieda je definovaná v balíku java.lang, ktorý je potrebné importovať, aby si naše triedy boli vedomé jej definície.

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

Vyššie uvedený príklad vytvára novú triedu Počítadlo ktorý rozširuje Závit triedy a má prednosť pred Thread.run () metóda pre vlastnú implementáciu. The run () metóda je miesto, kde všetka práca Počítadlo vlákno triedy je hotové. Rovnakú triedu je možné vytvoriť implementáciou Runnable:

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

Tu abstrakt run () metóda je definovaná v rozhraní Runnable a je implementovaná. Všimnite si, že máme inštanciu súboru Závit triedy ako premenná Počítadlo trieda. Jediný rozdiel medzi týmito dvoma metódami je ten, že implementáciou programu Runnable je väčšia flexibilita pri vytváraní triedy Počítadlo. V uvedenom príklade stále existuje príležitosť rozšíriť Počítadlo triedy, ak je to potrebné. Väčšina vytvorených tried, ktoré je potrebné spustiť ako vlákno, bude implementovať funkciu Runnable, pretože pravdepodobne rozširuje niektoré ďalšie funkcie z inej triedy.

Nemyslite si, že rozhranie Runnable robí pri vykonávaní vlákna nejakú skutočnú prácu. Je to iba trieda vytvorená s cieľom poskytnúť predstavu o dizajne Závit trieda. V skutočnosti je veľmi malý a obsahuje iba jednu abstraktnú metódu. Tu je definícia rozhrania Runnable priamo zo zdroja Java:

balík java.lang; verejné rozhranie Spustiteľné {public abstract void run (); }

To je všetko, čo je k rozhraniu Runnable. Rozhranie poskytuje iba návrh, podľa ktorého by sa triedy mali implementovať. V prípade spustiteľného rozhrania vynúti definíciu iba súboru run () metóda. Preto sa väčšina práce vykonáva v Závit trieda. Bližší pohľad na časť v definícii Závit trieda poskytne predstavu o tom, čo sa v skutočnosti deje:

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

Z vyššie uvedeného úryvku kódu je zrejmé, že trieda Thread implementuje aj rozhranie Runnable. Závit.run () skontroluje, či sa cieľová trieda (trieda, ktorá sa bude spúšťať ako vlákno) nerovná null, a potom vykoná run () metóda terča. Keď k tomu dôjde, run () metóda cieľa bude bežať ako jeho vlastné vlákno.

Štartovanie a zastavenie

Pretože sú teraz zrejmé rôzne spôsoby vytvorenia inštancie vlákna, budeme diskutovať o implementácii vlákien počnúc spôsobmi, ktoré sú k dispozícii na ich spustenie a zastavenie pomocou malého appletu obsahujúceho vlákno na ilustráciu mechaniky:

Príklad CounterThread a zdrojový kód

Vyššie uvedený applet začne počítať od 0 po zobrazenie jeho výstupu na obrazovku aj na konzolu. Krátky pohľad môže vytvoriť dojem, že program začne počítať a zobrazovať každé číslo, ale nie je to tak. Bližšie preskúmanie vykonania tohto appletu odhalí jeho skutočnú identitu.

V takom prípade CounterThread trieda bola nútená implementovať Runnable, pretože rozšírila triedu Applet. Rovnako ako vo všetkých appletoch, aj init () najskôr sa vykoná metóda. V init (), je premenná Count inicializovaná na nulu a nová inštancia súboru Závit trieda je vytvorená. Prejdením toto do Závit konštruktor, nové vlákno bude vedieť, ktorý objekt sa má spustiť. V tomto prípade toto je odkaz na CounterThread. Po vytvorení vlákna je potrebné ho spustiť. Výzva na štart () zavolá cieľ run () metóda, ktorá je CounterThread.run (). Výzva na štart () sa vráti okamžite a vlákno sa začne vykonávať súčasne. Všimnite si, že run () metóda je nekonečná slučka. Je to nekonečné, pretože kedysi run () metóda ukončí, vlákno sa zastaví. The run () metóda zvýši premennú Count, sleep na 10 milisekúnd a odošle požiadavku na obnovenie zobrazenia appletu.

Všimnite si, že je dôležité spať niekde vo vlákne. Ak nie, vlákno spotrebuje všetok čas CPU na proces a neumožní vykonanie akýchkoľvek iných metód, napríklad vlákien. Ďalším spôsobom, ako zastaviť vykonávanie vlákna, je zavolať na stop () metóda. V tomto príklade sa vlákno zastaví, keď sa kurzor nachádza v applete, keď stlačíte myš. V závislosti od rýchlosti počítača, na ktorom applet beží, sa nezobrazí každé číslo, pretože zvyšovanie sa deje nezávisle od maľovania appletu. Aplet sa nedá aktualizovať pri každej požiadavke, takže operačný systém zoradí fronty požiadaviek a následné obnovovacie požiadavky budú spokojné s jedným obnovením. Aj keď sa obnovy zaraďujú do frontu, počet sa stále zvyšuje, ale nezobrazuje sa.

Pozastavenie a obnovenie činnosti

Akonáhle je vlákno zastavené, nie je možné ho znovu spustiť pomocou štart () velenie, keďže stop () ukončí vykonávanie vlákna. Namiesto toho môžete pozastaviť vykonávanie vlákna pomocou spánok () metóda. Vlákno bude určitý čas spať a potom sa začne vykonávať po dosiahnutí časového limitu. To však nie je ideálne, ak je potrebné vlákno spustiť, keď dôjde k určitej udalosti. V takom prípade pozastaviť () metóda umožňuje vláknu dočasne zastaviť vykonávanie a pokračovať() metóda umožňuje opätovné spustenie zaveseného vlákna. Nasledujúci applet zobrazuje vyššie uvedený príklad upravený tak, aby applet pozastavil a obnovil jeho činnosť.

verejná trieda CounterThread2 rozširuje Applet implementuje Runnable {Thread t; int Count; booleovský pozastavený; public boolean mouseDown (Udalosť e, int x, int y) {if (pozastavené) t.resume (); else t.suspend (); pozastavené =! pozastavené; návrat pravdivý; } ...}

Príklad a zdrojový kód CounterThread2

Ak chcete sledovať aktuálny stav appletu, boolovská premenná pozastavené sa používa. Rozlišovanie rôznych stavov appletu je dôležité, pretože niektoré metódy spôsobia výnimky, ak sa vyvolajú v nesprávnom stave. Napríklad, ak bol applet spustený a zastavený, vykonanie štart () metóda hodí IllegalThreadStateException výnimkou.

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