Programiranje

Java 101: Razumevanje niti Java, 1. del: Predstavitev niti in izvedljivih datotek

Ta članek je prvi v štirih delih Java 101 serija, ki raziskuje niti Java. Čeprav se vam morda zdi, da bi bilo navoje v Javi težko izkoristiti, vam nameravam pokazati, da je niti enostavno razumeti. V tem članku vam predstavljam niti Java in tekače. V naslednjih člankih bomo raziskali sinhronizacijo (prek ključavnic), težave s sinhronizacijo (kot je blokada), mehanizem čakanja / obveščanja, razporejanje (s prednostjo in brez nje), prekinitev niti, časovnike, nestanovitnost, skupine niti in lokalne spremenljivke .

Upoštevajte, da je bil ta članek (del arhiva JavaWorld) maja 2013 posodobljen z novimi seznami kod in naložljivo izvorno kodo.

Razumevanje niti Java - preberite celotno serijo

  • 1. del: Predstavljanje niti in izvedljivih datotek
  • 2. del: Sinhronizacija
  • 3. del: Načrtovanje niti in čakanje / obveščanje
  • 4. del: Skupine niti in volatilnost

Kaj je nit?

Konceptualno pojem a nit ni težko dojeti: gre za neodvisno pot izvajanja s programsko kodo. Ko se izvede več niti, se pot ene niti skozi isto kodo običajno razlikuje od drugih. Denimo, da ena nit izvede bajtno kodo, enakovredno stavku if-else če del, medtem ko druga nit izvaja bajtno kodo, enakovredno datoteki drugače del. Kako JVM spremlja izvajanje vsake niti? JVM daje vsaki niti svoj sklad za klic metode. Poleg sledenja trenutnim navodilom bajtne kode, sklad klica metode spremlja tudi lokalne spremenljivke, parametre, ki jih JVM posreduje metodi, in vrnjeno vrednost metode.

Ko več niti izvaja zaporedja ukazov bajtne kode v istem programu, je to dejanje znano kot večnitnost. Večnitnost koristi programu na različne načine:

  • Programi z večnitnim grafičnim uporabniškim vmesnikom (grafični uporabniški vmesnik) ostajajo odzivni na uporabnike, medtem ko opravljajo druge naloge, kot je reciginiranje ali tiskanje dokumenta.
  • Navojni programi se običajno končajo hitreje kot njihovi nenavojni kolegi. To še posebej velja za niti, ki se izvajajo na večprocesorskem stroju, kjer ima vsaka nit svoj procesor.

Java doseže večnitnost skozi svoj java.lang.Nit razred. Vsak Navoj objekt opisuje posamezno nit izvajanja. Ta izvršitev se zgodi v Navojje teči () metoda. Ker privzeto teči () metoda ne naredi ničesar, morate podrazred Navoj in razveljavi teči () opraviti koristno delo. Za okus niti in večnitnost v kontekstu Navoj, preglejte seznam 1:

Seznam 1. ThreadDemo.java

// ThreadDemo.java razred ThreadDemo {public static void main (String [] args) {MyThread mt = new MyThread (); mt.start (); za (int i = 0; i <50; i ++) System.out.println ("i =" + i + ", i * i =" + i * i); }} razred MyThread razširja Thread {public void run () {for (int count = 1, row = 1; row <20; row ++, count ++) {for (int i = 0; i <count; i ++) System.out. natisni ('*'); System.out.print ('\ n'); }}}

Seznam 1 predstavlja izvorno kodo aplikaciji, ki jo sestavljajo razredi ThreadDemo in MyThread. Razred ThreadDemo poganja aplikacijo z ustvarjanjem MyThread objekt, zažene nit, ki se poveže s tem predmetom, in izvede nekaj kode za tiskanje tabele kvadratov. V nasprotju, MyThread preglasi Navojje teči () metoda za tiskanje (na standardni izhodni tok) pravokotnega trikotnika, sestavljenega iz zvezdic.

Načrtovanje niti in JVM

Večina (če ne vseh) izvedb JVM uporablja zmožnosti navojev osnovne platforme. Ker so te zmogljivosti specifične za platformo, se lahko vrstni red izhodov večnitnih programov razlikuje od vrstnega reda izhodov nekoga drugega. Ta razlika je posledica razporejanja, teme, ki jo bom raziskal kasneje v tej seriji.

Ko tipkate java ThreadDemo za zagon aplikacije JVM ustvari začetno nit izvajanja, ki izvede datoteko glavni () metoda. Z izvršitvijo mt.start ();, začetna nit pove JVM, da ustvari drugo nit izvajanja, ki izvrši navodila bajtne kode, ki vsebujejo MyThread predmeta teči () metoda. Ko začetek () vrne, začetna nit izvede svoj za zanka za tiskanje tabele kvadratov, medtem ko nova nit izvaja datoteko teči () za tiskanje pravokotnega trikotnika.

Kako je videti rezultat? Teči ThreadDemo izvedeti. Opazili boste, da se izhod vsake niti ponavadi prepleta z izhodom druge. To je rezultat, ker obe niti pošljeta svoj izhod v isti standardni izhodni tok.

Razred Thread

Če želite vešče pisati večnitno kodo, morate najprej razumeti različne metode, ki sestavljajo Navoj razred. Ta razdelek raziskuje številne od teh metod. Natančneje, izvedeli boste o metodah za zagon niti, poimenovanju niti, uspavanju niti, ugotavljanju, ali je nit živa, povezovanju ene niti z drugo niti in naštevanju vseh aktivnih niti v skupini niti podskupin trenutne niti. Tudi jaz razpravljam Navojpripomočki za odpravljanje napak in uporabniške niti v primerjavi z nitmi demona.

Predstavil bom preostanek Navojmetode v naslednjih člankih, z izjemo opuščenih Sunovih metod.

Zastarele metode

Sun je opuščal številne Navoj metode, kot so suspend () in Nadaljuj(), ker lahko zaklenejo vaše programe ali poškodujejo predmete. Posledično jih ne smete klicati v svoji kodi. Za rešitev teh metod glejte dokumentacijo SDK. V tej seriji ne pokrivam zastarelih metod.

Konstruiranje niti

Navoj ima osem konstruktorjev. Najenostavnejši so:

  • Nit (), ki ustvari a Navoj objekt s privzetim imenom
  • Nit (ime niza), ki ustvari a Navoj predmet z imenom, ki ga ime argument določa

Naslednji najpreprostejši konstruktorji so Navoj (cilj, ki ga je mogoče izvesti) in Nit (cilj, ki ga je mogoče izvesti, ime niza). Poleg Teče parametrov, so ti konstruktorji enaki prej omenjenim konstruktorjem. Razlika: Teče parametri identificirajo predmete zunaj Navoj ki zagotavljajo teči () metode. (Spoznate Teče kasneje v tem članku.) Končni štirje konstruktorji so podobni Nit (ime niza), Navoj (cilj, ki ga je mogoče izvesti), in Nit (cilj, ki ga je mogoče izvesti, ime niza); končni konstruktorji pa vključujejo tudi a ThreadGroup argument za organizacijske namene.

Eden od zadnjih štirih konstruktorjev, Nit (skupina ThreadGroup, ciljni cilj, ime niza, dolga velikost stack), je zanimiv s tem, da vam omogoča, da določite želeno velikost niza metod-klicnega sklada. Če lahko določite, da je ta velikost koristna v programih z metodami, ki uporabljajo rekurzijo - izvedbeno tehniko, s katero se metoda večkrat pokliče - za elegantno reševanje nekaterih težav. Z izrecno nastavitvijo velikosti sklada lahko včasih preprečite StackOverflowErrors. Vendar lahko prevelika velikost povzroči OutOfMemoryErrors. Sun tudi meni, da je velikost sklada klicev metode odvisna od platforme. Velikost sklada klicev metode se lahko spremeni, odvisno od platforme. Zato pred pisanjem kode, ki dobro kliče, dobro premislite o posledicah vašega programa Nit (skupina ThreadGroup, ciljni cilj, ime niza, dolga velikost stack).

Zaženite svoja vozila

Niti so podobne vozilom: programe premikajo od začetka do konca. Navoj in Navoj predmeti podrazreda niso niti. Namesto tega opisujejo atribute niti, na primer njeno ime, in vsebujejo kodo (prek teči () metoda), ki jo nit izvede. Ko pride čas za izvedbo nove niti teči (), druga nit pokliče Navojali predmet njegovega podrazreda začetek () metoda. Če želite na primer zagnati drugo nit, začetno nit aplikacije, ki se izvede glavni ()—Pokliče začetek (). V odgovor na to koda za obdelavo niti JVM deluje s platformo, da zagotovi, da se nit pravilno inicializira in pokliče a Navojali predmet njegovega podrazreda teči () metoda.

Enkrat začetek () dokonča, izvede več niti. Ker ponavadi razmišljamo linearno, pogosto težko razumemo to sočasno (sočasna) aktivnost, ki se pojavi, ko se izvajata dve ali več niti. Zato morate preučiti grafikon, ki prikazuje, kje se nit izvaja (njen položaj) glede na čas. Spodnja slika predstavlja takšen diagram.

Grafikon prikazuje več pomembnih časovnih obdobij:

  • Inicializacija začetne niti
  • V trenutku, ko se nit začne izvajati glavni ()
  • V trenutku, ko se nit začne izvajati začetek ()
  • Trenutek začetek () ustvari novo nit in se vrne v glavni ()
  • Inicializacija nove niti
  • V trenutku, ko se nova nit začne izvajati teči ()
  • Vsaka nit se konča v različnih trenutkih

Upoštevajte, da je inicializacija nove niti in njeno izvajanje teči (), in njegova prekinitev se zgodi hkrati z izvedbo začetne niti. Upoštevajte tudi, da po klicu niti začetek (), poznejši klici te metode pred teči () metoda zapusti vzrok začetek () metati a java.lang.IllegalThreadStateException predmet.

Kaj je v imenu?

Med sejo odpravljanja napak je uporabniško prijazno ločevanje ene niti od druge koristno. Za razlikovanje med nitmi Java poveže ime z nitjo. To ime je privzeto Navoj, vezaj in celoštevilsko število na osnovi nič. Privzeta imena niti Java lahko sprejmete ali pa izberete svojega. Če želite prilagoditi imena po meri, Navoj ponuja konstruktorje, ki sprejmejo ime argumenti in a setName (ime niza) metoda. Navoj ponuja tudi a getName () metoda, ki vrne trenutno ime. Seznam 2 prikazuje, kako vzpostaviti ime po meri s pomočjo Nit (ime niza) konstruktor in poiščite trenutno ime v teči () metoda s klicem getName ():

Seznam 2. NameThatThread.java

// NameThatThread.java razred NameThatThread {public static void main (String [] args) {MyThread mt; if (args.length == 0) mt = new MyThread (); sicer mt = nov MyThread (argumenti [0]); mt.start (); }} razred MyThread razširja Thread {MyThread () {// Prevajalnik ustvari bajtno kodno protivrednost super (); } MyThread (ime niza) {super (ime); // Posreduj ime Thread superclass} public void run () {System.out.println ("My name is:" + getName ()); }}

Neobvezni argument imena lahko posredujete MyThread v ukazni vrstici. Na primer, java NameThatThread X ugotavlja X kot ime niti. Če imena ne navedete, boste videli naslednji izhod:

Moje ime je: Thread-1

Če želite, lahko spremenite super (ime); pokličite MyThread (ime niza) konstruktor na klic setName (ime niza)-kot v setName (ime);. Ta zadnji klic metode doseže isti cilj - določitev imena niti - kot super (ime);. To vam pustim kot vajo.

Poimenovanje glavno

Java dodeli ime glavni v nit, ki poganja glavni () metoda, začetna nit. To ime običajno vidite v Izjema v niti "main" sporočilo, da se privzeti obravnavalec izjeme JVM natisne, ko začetna nit vrže predmet izjeme.

Spat ali ne spati

V nadaljevanju te kolumne vam bom predstavil animacija- večkratno risanje slik na eni površini, ki se med seboj nekoliko razlikujejo, da se doseže iluzija gibanja. Za izvedbo animacije se mora nit med prikazom dveh zaporednih slik ustaviti. Klicanje Navojje statičen spanje (dolgi milis) metoda prisili nit, da se zaustavi milis milisekund. Druga nit bi lahko prekinila spalno nit. Če se to zgodi, se spalna nit prebudi in vrže InterruptedException predmet iz spanje (dolgi milis) metoda. Kot rezultat, koda, ki kliče spanje (dolgi milis) mora biti prikazan znotraj poskusite blok - ali pa mora vključevati metoda kode InterruptedException v svojem meti klavzulo.

Za demonstracijo spanje (dolgi milis), Napisal sem CalcPI1 aplikacijo. Ta aplikacija zažene novo nit, ki uporablja matematični algoritem za izračun vrednosti matematične konstante pi. Medtem ko nova nit izračuna, se začetna nit zaustavi za 10 milisekund s klicem spanje (dolgi milis). Ko se začetna nit prebudi, natisne vrednost pi, ki jo nova nit shrani v spremenljivko pi. Seznam 3 predstavlja CalcPI1izvorna koda:

Seznam 3. CalcPI1.java

// CalcPI1.java razred CalcPI1 {public static void main (String [] args) {MyThread mt = new MyThread (); mt.start (); poskusite {Thread.sleep (10); // Spanje 10 milisekund} ulov (InterruptedException e) {} System.out.println ("pi =" + mt.pi); }} razred MyThread razširja Thread {boolean negative = true; dvojni pi; // Inicializira se na 0,0, privzeto javni void run () {for (int i = 3; i <100000; i + = 2) {if (negativen) pi - = (1,0 / i); sicer pi + = (1,0 / i); negativno =! negativno; } pi + = 1,0; pi * = 4,0; System.out.println ("Končan izračun PI"); }}

Če zaženete ta program, boste videli rezultate, podobne (vendar verjetno ne enake) naslednjim:

pi = -0,2146197014017295 Končan izračun PI
$config[zx-auto] not found$config[zx-overlay] not found