Többszálas java

Ebben a cikkben, foglalkozni fogok egy nagy és összetett téma a többszálas Java. Persze, minden egy cikkben nem tudom megmondani, így említem csak a legalapvetőbb témákat.


Az előnyök a multi-threading
Annak ellenére, hogy sok kihívással, többszálú nagyon hasznos felhasználói felületeket. Míg az egyik szál foglalkozik adatfeldolgozás, a második nyugodtan felhívja a menetrend. Ennek eredményeként látjuk a képernyőn, sima animáció, és kölcsönhatásba léphet felület elemeit félelem nélkül, hogy minden lefagy.

A többszálas is javítja a feldolgozási sebesség: míg az egyik előkészíti az adatáramlás, például szivattyúzás az interneten kis részletekben, második és harmadik patakok tudja feldolgozni azokat, és rögzítse az eredményt a negyedik meghajtót.

Ha van egy multi-core processzor, a patakok jelentősen javítja a teljesítményt azáltal, melyet más mag.


Nos, elég az elmélet, térjünk át a gyakorlatba.


létrehozása patakok
A Java, a streaming a téma osztályban. Kétféle módon lehet létrehozni egy új téma, de lehet írni, ha nagyszámú módon.

Az első lehetőség kiterjesztése a téma osztályban.

public class CustomThread kiterjed téma # 123;

Rendszer. ki. println # 40; 6 # 41; ;


Akkor is játszani körül a Reflection API, de ez tényleg, mint valami magukat.


Megszakítás szálak
Igen, ez megszakad, és nem hagyja abba, vagy szüneteltetheti. Streams nem lehet megállítani a szokásos módszerekkel, mert ez jár a sok probléma. Ezért a stop módszer, felfüggeszteni, szünet, és megsemmisíti a téma osztályban vannak megjelölve elavult. Tehát ne kell tennie mindent magam.

Általánosságban, egy Java szál fut, amíg a kód fut a módszer távon. Ha a roham egy egyszerű művelet, például a szöveges kimenet a konzolra, az áramlás közvetlenül a műtét után befejeződött. Ha nem vagyunk az áramlás rekord adatait a ciklus 100 fájl, a patak fog működni mindaddig, amíg elértünk a feladatukat.

És mellesleg, ha a szál fut a háttérben, és bezárja a programot, a folyamat a program továbbra is lógnak a háttérben, amíg nem teljesítették a szálakat. Hogy a végén a program garantáltan megkapja a befejezése áramlás, szükséges, hogy az áramlás a démon. Ez viccesen hangzik, de így van:

menet. setDaemon # 40; igaz # 41;


Szóval, van egy szál, amely végrehajtja a ciklusban néhány ismétlődő működését, és mi kell egy kis idő, hogy töltse ki. A legkézenfekvőbb módja az, hogy egy logikai változó, és ellenőrizze annak állapotát. Leggyakrabban erre:

privát logikai isRunning;

Az összes szál kész. Counter = 5000

Hogyan fog működni:
- Hoztunk létre két patakok és futtatni őket.
- Tegyük fel, hogy az első folyam indult gyorsabb és belépett a ciklus közben. Míg a második fut.
- Az első folyam blokk foltok szinkronizálva. Ellenőrzések - van-e most ebben a blokkban más szálak? Nem, így az első menet kerül a készülékbe. Második léptek a ciklus közben.
- Az első folyam most egy for ciklus számláló növekszik. A második folyam eléri a szinkronizált blokk. Ismét egy csekket, és az áramlási belsejében, engedélyt bemenni nem kapott, ezért a második menet vár.
- Az első folyam mindig a hurok. A második folyam mindig csak arra vár.
- Végül az első folyam kilép a hurok, és elhagyja a szinkronizálás területen. A második folyam kap engedélyt bemenni.

Így a szinkronizált működés folyik.


Szinkronblokkot kell helyezni bölcsen. Ha volt valami, mint ez:

private static void printCounter # 40; # 41; # 123;

szinkronizált # 40; Counter. osztály # 41; # 123;

míg # 40; Counter. kap # 40; # 41; <5000 ) {

mert # 40; int i = 0; én <10 ; i ++ ) {

Counter. növekedés # 40; # 41; ;

Menet. alvás # 40; 1 # 41; ;

# 125; fogás # 40; InterruptedException ex # 41; # 123;

Menet. currentThread # 40; # 41;. szakítsa # 40; # 41; ;


mi lenne már megvan a megfelelő eredményt 5000, csak dolgozott csak egy szál:
- Készítsen két szál és futtatni őket.
- Tegyük fel, hogy az első folyam indult gyorsabb, és bement a időzítő egység. Míg a második fut.
- Az első folyam most while ciklus. A második folyam teljesül synchonized blokk, és nem kap beutazási engedélyt.
- Az első folyam fut. Második várakozás.
- Miután néhány időt, az első áramlásmérő nőtt 5000 ciklusok és balra és szinkron blokk. A második áramot szabad bemenni.
- Az első folyam befejeződött. A második áramot ellenőrizzük, hogy Counter.get feltétel () <5000 уже не выполняется и не вошёл в цикл while. Покинул блок синхронизации и завершился.


A másik megoldás, hogy a probléma a mérő - és ezzel a get és a növedék szinkronizálva. Aztán szinkron blokk a távon módszer nem szükséges.

public class SynchronizedCounter # 123;

privát static int számláló = 0;


Azaz, akkor menj be a szinkronizáló egység, monitor. A visszatérési érték, és lépjen ki a monitort. Ha a kritikus szakasz kivétel volt, mi is jön ki a monitort.

A szinkronizált módszer bájtkódot utasítások és monitorenter monitorexit nem volt, de ez nem jelenti azt, hogy nincs bejegyzés a monitoron. Szinkron zászlót a JVM módszer azt mondja, hogy ezeket az utasításokat végre kell hajtani. Ez azt jelenti, hogy nem jelennek meg a kódot, de rejtve a JVM - még mindig végrehajtja őket.


Ami a jövőt illeti, bemutatjuk egy másik lehetséges megoldást. Sok osztályok a java.util.concurrent csomag a különböző többszálú használatra. Az egyik ilyen osztályok AtomicInteger. ami a működését a rendszám.

public class AtomicCounter # 123;

private static final AtomicInteger counter = new AtomicInteger # 40; # 41; ;

public static int get # 40; # 41; # 123;

visszatér számláló. kap # 40; # 41; ;

public static void növekmény # 40; # 41; # 123;

számláló. incrementAndGet # 40; # 41; ;


Most sehol sem kell hozzá egy szinkron blokk.
Másrészt osztályok, amelyek megkönnyítik dolgozni multi-threading, azt fogja mondani a következő cikkben.


A szinkronizálás. Példa tervezése egy többszálú alkalmazást
A tetején a cikk, meg akarom mutatni egy példát egy kis alkalmazás, és annak fontosságát, hogy a megfelelő tervezési áramlik.
Tegyük fel, hogy van 20 adatfájlokat. Meg kell olvasni a leghatékonyabb ezeket a fájlokat, és megjeleníti a képernyőn megjelenő adatokat. Ezek, ahogy olvasod, akkor felkerül a listára, és onnan látható.

Van egy osztály, amely felelős a rajz panel, ott fogunk felvenni tárgyakat, mint elolvasni:

privát PaintPanel panel;

private void processFile # 40; file # 41; # 123;

Rendszer. ki. printf # 40; "Process fájl% s% s menet \ n". fájlba. getName # 40; # 41;. Menet. currentThread # 40; # 41;. getName # 40; # 41; # 41; ;

megpróbál # 40; FileInputStream fis = new FileInputStream # 40; fájl # 41; ;

DataInputStream dis = new DataInputStream # 40; fis # 41; # 41; # 123;

míg # 40; dis. elérhető # 40; # 41> 0 # 41; # 123;

Triangle háromszög = Triangle. olvas # 40; dis # 41; ;

panel. addTriangle # 40; háromszög # 41; ;

Menet. alvás # 40; 1 # 41; ;

# 125; fogás # 40; IOException | InterruptedException azaz # 41; # 123;


Most nincs hiba, de a feldolgozás 20 fájl körülbelül öt percig. Ráadásul az első fájlokat gyorsan olvasni, majd lelassul. Próbáld megérteni, hogy ez miért történik.


▌ 2. opció egy fájl - egy szál

20 szál logikus feltételezni, hogy a munka kerül sor 20-szer gyorsabb. Ez lett volna viselnie a processzor legalább 20 magot. Ellenkező esetben csak létre további stresszt és az összes adatot nem lehet levonni nagyon simán.

lista szálak = új ArrayList <> # 40; fájlokat. hossz # 41; ;

mert # 40; Fájl fájlt. fájlok # 41; # 123;

Menet menet = új téma # 40; # 40; # 41; -> processFile # 40; fájl # 41; # 41; ;

szálak. hozzáad # 40; cérna # 41; ;

menet. kezdet # 40; # 41; ;

mert # 40; Menet menet. menetek # 41; # 123;

menet. csatlakozik # 40; # 41; ;

# 125; fogás # 40; InterruptedException e # 41; # 123;

Menet. currentThread # 40; # 41;. szakítsa # 40; # 41; ;


Mindazonáltal a munka ma már 40 másodperc alatt. Ez egy hosszú idő. Ugyanez a probléma, hogy lelassult a munka az első kiviteli, lassítja mindent most. Ott kell lennie egy módja annak, hogy megszabaduljunk a szinkronizált blokkok, és ez a módszer.


▌ 3. opció: szinkronizált lista

Annak érdekében, hogy szinkronizálja a szokásos lista, hívja a módszer

Gyűjtemények. synchronizedList # 40; lista # 41;


Most kap egy listát, amelyben az összes módszer szinkronizálva. De, sajnos, a bejáró, hogy használják a foreach így nem fordulhat szinkronizálni, és mi lesz, hogy csavarja be egy szinkronizált blokk, vagy elhagyni azt a mellett egy egyszerű felsorolás elemeinek a hurok.

public class PaintPanelSynchronizedList kiterjed PaintPanel # 123;

privát végleges listája háromszögek;

nyilvános PaintPanelSynchronizedList # 40; int szélesség, magasság int # 41; # 123;

szuper # 40; szélesség, magasság # 41; ;

háromszögek = gyűjtemények. synchronizedList # 40; új ArrayList <> # 40; # 41; # 41; ;


▌ 4. megvalósítás számának korlátozása adatfolyamok

Mi korlátozhatja a szálak száma létrehozni egy medence:

ExecutorService es = Végrehajtók. newFixedThreadPool # 40; maxThreads # 41; ;


Most is létrehozhat legalább száz szál fut egy időben nem lesz több maxThreads folyik.

Korlátozza a medence öt szál

ExecutorService es = Végrehajtók. newFixedThreadPool # 40; 5 # 41; ;

mert # 40; Fájl fájlt. fájlok # 41; # 123;

es. kivégez # 40; # 40; # 41; -> processFile # 40; fájl # 41; # 41; ;

es. üzemszünet # 40; # 41; ;

es. awaitTermination # 40; 2. TimeUnit. JEGYZŐKÖNYV # 41; ;

# 125; fogás # 40; InterruptedException e # 41; # 123;

Menet. currentThread # 40; # 41;. szakítsa # 40; # 41; ;


Most, a végrehajtási idő nőtt 11 másodperc, de a renderelés zökkenőmentesen történjen, és biztosak lehetünk benne, hogy ha van több száz kép, a rendszer nem kap nagy terhelést.

A legjobb gyakorlat az, hogy korlátozza a szál medence a processzorok száma a rendszerben:

Végrehajtók. newFixedThreadPool # 40; Runtime. getRuntime # 40; # 41;. availableProcessors # 40; # 41; # 41; ;


▌ Option 5. java.util.concurrent

Kapcsolódó cikkek