Uvod u Java niti

Ovaj članak, jedan od prvih koje je objavio JavaWorld, opisuje kako se niti implementiraju u programski jezik Java, počevši od općeg pregleda niti.

Jednostavno rečeno, nit je put izvršenja programa. Većina danas napisanih programa radi se kao jedna nit, što uzrokuje probleme kada se istovremeno treba dogoditi više događaja ili radnji. Recimo, na primjer, program nije sposoban crtati slike dok čita tipke. Program mora posvetiti punu pažnju ulazu s tipkovnice kojem nedostaje sposobnost obrade više događaja odjednom. Idealno rješenje ovog problema je istodobno izvršavanje dva ili više odjeljaka programa. Niti nam to omogućuju.

Učenje o Java nitima

Ovaj je članak dio arhive tehničkog sadržaja JavaWorld. Pogledajte sljedeće da biste saznali više o Java nitima i istodobnosti:

Razumijevanje Java niti ( Java 101 serija, 2002):

  • Dio 1: Upoznavanje niti i pokretačkih programa
  • Dio 2: Sinkronizacija niti
  • Dio 3: Zakazivanje niti i pričekajte / obavijestite
  • Dio 4: Skupine niti i volatilnost

Povezani članci

  • Java s hipernitnim nitima: Korištenje Java Concurrency API (2006)
  • Bolji monitori za višenitne programe (2007)
  • Razumijevanje istodobnosti glumca, 1. dio (2009.)
  • Otkrivanje i rukovanje visećim nitima (2011)

Također provjerite JavaWorld web stranicu i tražilicu .

Višenitne aplikacije pružaju svoju snažnu snagu istodobnim pokretanjem mnogih niti unutar jednog programa. S logičke točke gledišta, višenitnost znači da se istovremeno može izvršiti više redaka pojedinog programa, međutim, to nije isto što i pokretanje programa dva puta i reći da se istovremeno izvršava više redaka programa vrijeme. U ovom slučaju, operativni sustav programe tretira kao dva odvojena i različita procesa. Pod Unixom, forking proces stvara podređeni proces s različitim adresnim prostorom i za kôd i za podatke. Međutim,fork()stvara puno dodatnih troškova za operativni sustav, što ga čini vrlo CPU-intenzivnim radom. Pokretanjem niti umjesto toga stvara se učinkovit put izvršenja, a istovremeno dijeli izvorno podatkovno područje od roditelja. Ideja dijeljenja podatkovnog područja vrlo je korisna, ali otvara neka zabrinjavajuća područja o kojima ćemo kasnije razgovarati.

Stvaranje niti

Kreatori Jave su ljubazno osmislili dva načina stvaranja niti: implementaciju sučelja i proširenje klase. Proširenje klase je način na koji Java nasljeđuje metode i varijable od roditeljske klase. U ovom se slučaju može proširiti ili naslijediti samo s jednim roditeljskim razredom. Ovo ograničenje unutar Jave može se prevladati primjenom sučelja, što je najčešći način stvaranja niti. (Imajte na umu da čin nasljeđivanja dopušta samo izvođenje klase kao niti. Na klasi je start()izvršenje, itd.)

Sučelja pružaju način da programeri postave temelje klase. Koriste se za oblikovanje zahtjeva za niz klasa koje treba implementirati. Sučelje postavlja sve, a klasa ili razredi koji implementiraju sučelje odrađuju sav posao. Različiti skupovi klasa koji implementiraju sučelje moraju slijediti ista pravila.

Postoji nekoliko razlika između klase i sučelja. Prvo, sučelje može sadržavati samo apstraktne metode i / ili statičke završne varijable (konstante). Klase, s druge strane, mogu implementirati metode i sadržavati varijable koje nisu konstante. Drugo, sučelje ne može implementirati nikakve metode. Klasa koja implementira sučelje mora implementirati sve metode definirane u tom sučelju. Sučelje se može proširiti iz drugih sučelja i (za razliku od klasa) može se proširiti iz više sučelja. Nadalje, sučelje se ne može instancirati s novim operatorom; na primjer, Runnable a=new Runnable();nije dopušteno.

Prva metoda stvaranja niti je jednostavno proširivanje iz Threadklase. Učinite to samo ako klasu koju trebate izvršiti kao nit nikada ne treba proširiti iz druge klase. ThreadKlasa je definirana u paketu java.lang, koji treba da se uvoze, tako da su naši razredi su svjesni svoje definicije.

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

Gornji primjer stvara novu klasu Counterkoja proširuje Threadklasu i nadjačava Thread.run()metodu za vlastitu implementaciju. run()Metoda je mjesto gdje sve djelo Counterje učinjeno klase niti. Ista klasa može se stvoriti implementacijom Runnable:

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

Ovdje je apstraktna run()metoda definirana u Runnable sučelju i implementira se. Imajte na umu da imamo primjerak Threadklase kao varijablu Counterklase. Jedina razlika između ove dvije metode je što je implementacijom Runnablea veća fleksibilnost u stvaranju klase Counter. U gornjem primjeru još uvijek postoji mogućnost da se Counterrazred produži , ako je potrebno. Većina klasa stvorenih koje treba pokretati kao nit implementirat će Runnable jer vjerojatno proširuju neke druge funkcije iz druge klase.

Nemojte misliti da sučelje Runnable radi bilo kakav stvarni posao kada se nit izvršava. To je samo razred stvoren da pruži ideju o dizajnu Threadrazreda. Zapravo je vrlo mala koja sadrži samo jednu apstraktnu metodu. Evo definicije Runnable sučelja izravno iz Java izvora:

package java.lang; public interface Runnable { public abstract void run(); }

To je sve što omogućuje Runnable sučelje. Sučelje pruža samo dizajn na kojem bi se klase trebale implementirati. U slučaju Runnable sučelja, nameće definiciju samo run()metode. Stoga se većina posla obavlja u Threadnastavi. Pobliži pogled na odjeljak u definiciji Threadklase dat će ideju o tome što se stvarno događa:

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

Iz gornjeg isječka koda vidljivo je da klasa Thread također implementira Runnable sučelje. Thread. run()provjerava je li ciljna klasa (klasa koja će se izvoditi kao nit) nije jednaka nuli, a zatim izvršava run()metodu cilja. Kada se to dogodi, run()metoda cilja će se izvoditi kao vlastita nit.

Pokretanje i zaustavljanje

Budući da su sada vidljivi različiti načini stvaranja instance niti, razgovarat ćemo o provedbi niti počevši od načina dostupnih za njihovo pokretanje i zaustavljanje pomoću malog apleta koji sadrži nit za ilustraciju mehanike:

Primjer CounterThreada i izvorni kod

Gornji će aplet početi računati od 0 prikazujući svoj izlaz i na zaslonu i na konzoli. Brzi pogled mogao bi stvoriti dojam da će program početi brojati i prikazivati ​​svaki broj, ali to nije slučaj. Pažljivije ispitivanje izvršenja ovog apleta otkrit će njegov pravi identitet.

U ovom je slučaju CounterThreadklasa bila prisiljena implementirati Runnable jer je proširila klasu Applet. Kao i u svim apletima, init()metoda se izvršava prva. U init(), varijabla Count inicijalizira se na nulu i Threadkreira se nova instanca klase. Prelaskom thisna Threadkonstruktor, nova nit će znati koji objekt treba pokrenuti. U ovom je slučaju thisreferenca na CounterThread. Nakon stvaranja niti treba je pokrenuti. Poziv na start()pozvat će ciljnu run()metodu koja je CounterThread. run(). Poziv na start()vratit će se odmah i nit će se početi izvršavati u isto vrijeme. Primijetite da je run()metoda beskonačna petlja. Beskonačno je jer jednomrun()metoda izlazi, nit prestaje s izvršavanjem. run()Način povećat će varijabla broj, spavati za 10 milisekundi i poslati zahtjev za osvježavanje prikaza applet-a.

Imajte na umu da je važno spavati negdje u niti. Ako nije, nit će potrošiti sve procesorsko vrijeme za proces i neće dopustiti izvršenje bilo kojih drugih metoda poput niti. Drugi način zaustavljanja izvođenja niti je pozivanje stop()metode. U ovom se primjeru nit zaustavlja pritiskom miša dok je kursor u apletu. Ovisno o brzini računala na kojem aplet radi, neće se prikazati svaki broj, jer se povećavanje vrši neovisno o slikanju apleta. Aplet se ne može osvježiti na svaki zahtjev, tako da će OS postavljati zahtjeve u red čekanja, a uzastopni zahtjevi za osvježavanjem zadovoljit će se jednim osvježavanjem. Dok se osvježavanja stavljaju u red čekanja, brojanje se i dalje povećava, ali se ne prikazuje.

Obustavljanje i nastavak

Once a thread is stopped, it cannot be restarted with the start() command, since stop() will terminate the execution of a thread. Instead you can pause the execution of a thread with the sleep() method. The thread will sleep for a certain period of time and then begin executing when the time limit is reached. But, this is not ideal if the thread needs to be started when a certain event occurs. In this case, the suspend() method allows a thread to temporarily cease executing and the resume() method allows the suspended thread to start again. The following applet shows the above example modified to suspend and resume the applet.

public class CounterThread2 extends Applet implements Runnable { Thread t; int Count; boolean suspended; public boolean mouseDown(Event e,int x, int y) { if(suspended) t.resume(); else t.suspend(); suspended = !suspended; return true; } ... }

CounterThread2 Example and Source code

Za praćenje trenutnog stanja apleta koristi se logička varijabla suspended. Razlikovanje različitih stanja apleta važno je jer će neke metode izbaciti iznimke ako su pozvane dok su u pogrešnom stanju. Na primjer, ako je aplet pokrenut i zaustavljen, izvršavanje start()metode izbacit će IllegalThreadStateExceptioniznimku.