Daugėjant sudėtingų tuo pačiu metu esančių programų, daugelis kūrėjų mano, kad „Java“ žemo lygio siūlų gavimo galimybės yra nepakankamos jų programavimo poreikiams tenkinti. Tokiu atveju gali būti laikas atrasti „Java Concurrency Utilities“. Pradėkite java.util.sąlyga
, su išsamiu Jeffo Frieseno įvedimu į „Executor“ sistemą, sinchronizatorių tipus ir „Java Concurrent Collections“ paketą.
„Java 101“: nauja karta
Pirmasis šios naujos „JavaWorld“ serijos straipsnis supažindina su „Java“ datos ir laiko API.
„Java“ platforma suteikia žemo lygio siūlų gavimo galimybes, leidžiančias kūrėjams rašyti vienu metu veikiančias programas, kai skirtingos gijos vykdomos vienu metu. Standartinis „Java“ sriegimas turi keletą trūkumų:
- „Java“ žemo lygiagretumo primityvai (
sinchronizuotas
,nepastovus
,laukti()
,pranešti ()
irpranešti visiems ()
) nėra lengva teisingai naudoti. Taip pat sunku aptikti ir ištaisyti sriegimo pavojus, tokius kaip aklavietė, siūlų badas ir lenktynių sąlygos, atsirandantys dėl neteisingo primityvų naudojimo. - Pasikliaujant
sinchronizuotas
koordinuojant prieigą tarp gijų, kyla našumo problemų, turinčių įtakos programų masteliui, reikalaujančiam daugelio šiuolaikinių programų. - Pagrindinės „Java“ siūlų gavimo galimybės yra taip pat žemas lygis. Kūrėjams dažnai reikia aukštesnio lygio konstrukcijų, tokių kaip semaforai ir gijų telkiniai, kurių „Java“ žemo lygio gijimo galimybės nesiūlo. Todėl kūrėjai kurs savo konstrukcijas, o tai užima daug laiko ir yra linkusi į klaidas.
„JSR 166: Concurrency Utilities“ sistema buvo sukurta siekiant patenkinti aukšto lygio siūlų įrengimo poreikį. Pradėta 2002 m. Pradžioje, sistema buvo įforminta ir įdiegta po dvejų metų programoje „Java 5“. Buvo patobulinta „Java 6“, „Java 7“ ir būsimoje „Java 8“.
Ši dviejų dalių „Java 101“: nauja karta serija supažindina programinės įrangos kūrėjus, susipažinusius su pagrindiniais „Java“ sriegiais, su „Java Concurrency Utilities“ paketais ir sistema. 1 dalyje pateikiu „Java Concurrency Utilities“ sistemos apžvalgą ir pristatau jos „Executor“ sistemą, sinchronizavimo įrankius ir „Java Concurrent Collections“ paketą.
Suprasti „Java“ gijas
Prieš pasinerdami į šią seriją, įsitikinkite, kad esate susipažinę su sriegimo pagrindais. Pradėkite nuo „Java 101“ įvadas į „Java“ žemo lygio siūlų gavimo galimybes:
- 1 dalis. Pristatome siūlus ir bėgimus
- 2 dalis. Siūlų sinchronizavimas
- 3 dalis: Gijos planavimas, laukimas / pranešimas ir gijos nutraukimas
- 4 dalis. Gijų grupės, nepastovumas, vietiniai siūlų kintamieji, laikmačiai ir gijų mirtis
„Java Concurrency Utilities“ viduje
„Java Concurrency Utilities“ sistema yra tipai suprojektuoti naudoti kaip statybiniai elementai kuriant lygiagrečias klases ar programas. Šie tipai yra saugūs siūlams, buvo kruopščiai išbandyti ir pasižymi dideliu našumu.
„Java Concurrency Utilities“ tipai yra suskirstyti į mažas sistemas; būtent vykdytojo pagrindai, sinchronizatorius, gretutinės kolekcijos, spynos, atominiai kintamieji ir „Fork / Join“. Jie yra toliau suskirstyti į pagrindinį paketą ir porą pakuočių:
- java.util.sąlyga yra aukšto lygio paslaugų tipai, kurie paprastai naudojami programuojant vienu metu. Tokie pavyzdžiai gali būti semaforai, barjerai, siūlų telkiniai ir gretutiniai maišos.
- java.util.concurrent.atomic pakete yra žemo lygio komunalinių paslaugų klasės, kurios palaiko vienų kintamųjų programavimą be užrakto.
- java.util.concurrent.locks pakete yra žemo lygio naudingumo tipai, skirti užrakinti ir laukti sąlygų, kurios skiriasi nuo „Java“ žemo lygio sinchronizavimo ir monitorių naudojimo.
„Java Concurrency Utilities“ sistema taip pat atskleidžia žemą lygį palyginti ir keistis (CAS) techninės įrangos instrukcija, kurios variantus dažniausiai palaiko šiuolaikiniai procesoriai. CAS yra daug lengvesnis už „Java“ monitoriais pagrįstą sinchronizavimo mechanizmą ir yra naudojamas kai kurioms labai keičiamoms lygiagrečioms klasėms įgyvendinti. CAS pagrindu java.util.concurrent.locks.ReentrantLock
Pavyzdžiui, klasė yra našesnė už lygiavertį monitorių sinchronizuotas
primityvus. „ReentrantLock“
siūlo didesnį užrakinimo valdymą. (2 dalyje paaiškinsiu daugiau apie tai, kaip veikia CAS java.util.sąlyga
.)
System.nanoTime ()
Į „Java Concurrency Utilities“ sistemą įeina ilgas „nanoTime“ ()
, kuris yra java.lang.Sistema
klasė. Šis metodas suteikia prieigą prie nanosekundžių granuliuotumo laiko šaltinio, kad būtų galima atlikti santykinius laiko matavimus.
Kituose skyriuose aš pristatysiu tris naudingas „Java Concurrency Utilities“ funkcijas, pirmiausia paaiškindamas, kodėl jos yra tokios svarbios šiuolaikiniam lygiagrečiam laikui, ir tada pademonstruodamas, kaip jie veikia didindami vienu metu naudojamų „Java“ programų greitį, patikimumą, efektyvumą ir mastelį.
Vykdytojo sistema
Sriegiant a užduotis yra darbo vienetas. Viena iš žemo lygio siūlų „Java“ problemų yra ta, kad užduočių pateikimas yra glaudžiai susijęs su užduočių vykdymo politika, kaip rodo 1 sąrašas.
Sąrašas 1. Server.java (1 versija)
importuoti java.io.IOException; importuoti java.net.ServerSocket; importuoti java.net.Socket; klasės serveris {public static void main (String [] argumentai) meta IOException {ServerSocket socket = new ServerSocket (9000); while (tiesa) {final Socket s = socket.accept (); Vykdomas r = new Vykdomas () {@Paisyti viešą negaliojančią paleidimą () {doWork (s); }}; nauja gija (r) .start (); }} static void doWork („Socket s“) {}}
Aukščiau pateiktas kodas apibūdina paprastą serverio programą (su „doWork“ („Socket“)
trumpai paliekamas tuščias). Serverio gija pakartotinai skambina socket.accept ()
laukti gaunamos užklausos ir tada paleidžia giją, kad aptarnautų šią užklausą, kai ji bus pasiekta.
Kadangi ši programa sukuria naują kiekvienos užklausos giją, ji nėra labai menkė susidūrus su daugybe užklausų. Pvz., Kiekvienai sukurtai gijai reikalinga atmintis, o per daug gijų gali išeikvoti turimą atmintį ir priversti programą nutraukti.
Galite išspręsti šią problemą pakeisdami užduočių vykdymo politiką. Užuot visada kūrę naują giją, galite naudoti gijų grupę, kurioje fiksuotas gijų skaičius aptarnautų gaunamas užduotis. Tačiau norėdami atlikti šį pakeitimą, turėtumėte perrašyti programą.
java.util.sąlyga
apima „Executor“ sistemą, nedidelę tipų sistemą, kuri atsieja užduočių pateikimą nuo užduočių vykdymo strategijos. Naudojant „Executor“ sistemą galima lengvai suderinti programos užduočių vykdymo politiką nereikalaujant gerokai perrašyti kodo.
Vykdytojo sistemos viduje
Vykdytojo sistema remiasi Vykdytojas
sąsaja, kuri apibūdina vykdytojas kaip bet koks objektas, galintis įvykdyti java.lang. Bėgama
užduotys. Ši sąsaja deklaruoja šį pavienį metodą vykdant a Bėgama
užduotis:
void execute (vykdoma komanda)
Jūs pateikiate a Bėgama
užduotį ją perduodant vykdyti (paleidžiamas)
. Jei vykdytojas negali atlikti užduoties dėl kokių nors priežasčių (pavyzdžiui, jei vykdytojas buvo uždarytas), šis metodas RejectedExecutionException
.
Pagrindinė sąvoka yra ta užduoties pateikimas yra atsietas nuo užduoties vykdymo politikos, kurį apibūdina an Vykdytojas
įgyvendinimas. bėgama Taigi užduotis gali atlikti naudodama naują giją, sujungtą giją, skambinančią giją ir pan.
Prisimink tai Vykdytojas
yra labai ribotas. Pavyzdžiui, negalima uždaryti vykdytojo ar nustatyti, ar asinchroninė užduotis baigta. Taip pat negalite atšaukti vykdomos užduoties. Dėl šių ir kitų priežasčių „Executor“ sistema suteikia „ExecutorService“ sąsają, kuri tęsiasi Vykdytojas
.
Penki iš ExecutorService
metodai ypač verti dėmesio:
- loginis laukimas Baigimas (ilgasis skirtasis laikas, „TimeUnit“ vienetas) užblokuoja skambinančią giją, kol visos užduotys bus įvykdytos po išjungimo užklausos, atsiras skirtasis laikas arba dabartinė gija bus nutraukta, atsižvelgiant į tai, kas įvyks anksčiau. Maksimalų laukimo laiką nurodo
laikas baigėsi
, ir ši vertė išreiškiamavienetas
vienetų, nurodytų„TimeUnit“
enum; pavyzdžiui,„TimeUnit“. SEKUNDĖS
. Šis metimas metajava.lang.InterruptedException
nutraukus dabartinę giją. Tai grįžta tiesa kai vykdytojas nutraukiamas ir melagingas kai baigiasi skirtasis laikas iki nutraukimo. - loginis isShutdown () grįžta tiesa kai vykdytojas bus uždarytas.
- negaliojantis išjungimas () inicijuoja tvarkingą išjungimą, kurio metu vykdomos anksčiau pateiktos užduotys, tačiau nepriimamos naujos užduotys.
- Būsimas pateikimas (iškviečiama užduotis) pateikia vykdyti vertę grąžinančią užduotį ir grąžina a
Ateitis
atspindintys laukiančius užduoties rezultatus. - Būsimas pateikimas (vykdoma užduotis) pateikia a
Bėgama
užduotį vykdyti ir grąžina aAteitis
atstovaujantis tą užduotį.
Ateitis
sąsaja atspindi asinchroninio skaičiavimo rezultatą. Rezultatas žinomas kaip a ateityje nes paprastai jis nebus prieinamas tik kurį laiką ateityje. Galite pasinaudoti metodais, kaip atšaukti užduotį, grąžinti užduoties rezultatą (laukti neribotą laiką arba, kol baigsis uždelsimas, kai užduotis nebaigs) ir nustatyti, ar užduotis atšaukta, ar baigta.
Skambinama
sąsaja yra panaši į Bėgama
sąsaja, nes ji pateikia vieną metodą, apibūdinantį vykdytiną užduotį. Skirtingai Bėgama
's niekinis bėgimas ()
metodas, Skambinama
's V skambutis () išmeta Išimtį
metodas gali grąžinti vertę ir išimtis.
Vykdytojo gamyklos metodai
Tam tikru momentu norėsite įsigyti vykdytoją. „Executor“ sistema teikia Vykdytojai
šiam tikslui naudingumo klasė. Vykdytojai
siūlo keletą gamyklinių metodų, kaip gauti įvairių rūšių vykdytojus, kurie siūlo konkrečią gijų vykdymo politiką. Štai trys pavyzdžiai:
- „ExecutorService newCachedThreadPool“ () sukuria gijų grupę, kuri, jei reikia, sukuria naujas gijas, tačiau, kai jos yra, pakartotinai panaudoja anksčiau sukurtas gijas. Gijos, kurios nebuvo naudojamos 60 sekundžių, nutraukiamos ir pašalinamos iš talpyklos. Šis gijų fondas paprastai pagerina programų, kurios vykdo daug trumpalaikių asinchroninių užduočių, našumą.
- ExecutorService newSingleThreadExecutor () sukuria vykdytoją, kuris naudoja vieną darbuotojo giją, veikiančią be neribotos eilės - užduotys įtraukiamos į eilę ir vykdomos nuosekliai (vienu metu aktyvi ne daugiau kaip viena užduotis). Jei ši gija nutrūksta dėl gedimo vykdant prieš išjungiant vykdytoją, bus sukurta nauja gija, kuri užims vietą, kai reikės atlikti paskesnes užduotis.
- „ExecutorService newFixedThreadPool“ („int nThreads“) sukuria gijų grupę, kuri pakartotinai naudoja fiksuotą gijų skaičių, veikiantį bendroje neribotoje eilėje. Labiausiai
nSriegiai
gijos aktyviai apdoroja užduotis. Jei papildomos užduotys pateikiamos, kai visos gijos yra aktyvios, jos laukia eilėje, kol bus gija. Jei kuri nors gija nutrūksta dėl gedimo vykdant prieš išjungiant, bus sukurta nauja gija, kuri užims vietą, kai reikės atlikti kitas užduotis. Baseino gijos egzistuoja tol, kol vykdytojas bus uždarytas.
„Executor“ sistema siūlo papildomų tipų (tokių kaip ScheduledExecutorService
sąsaja), tačiau tipai, su kuriais greičiausiai dirbsite ExecutorService
, Ateitis
, Skambinama
ir Vykdytojai
.
Žr java.util.sąlyga
Javadoc tirti papildomus tipus.
Darbas su „Executor“ sistema
Jūs pastebėsite, kad „Executor“ sistemą yra gana lengva dirbti. 2 sąraše aš naudojau Vykdytojas
ir Vykdytojai
pakeisti serverio pavyzdį iš 1 sąrašo labiau pakeičiama gijų telkinio pagrindu.
Listing 2. Server.java (2 versija)
importuoti java.io.IOException; importuoti java.net.ServerSocket; importuoti java.net.Socket; importuoti java.util.concurrent.Executor; importuoti java.util.concurrent.Vykdytojai; klasės serveris {static Executor pool = Executors.newFixedThreadPool (5); public static void main (String [] args) meta IOException {ServerSocket socket = new ServerSocket (9000); while (tiesa) {final Socket s = socket.accept (); Vykdomas r = new Vykdomas () {@Paisyti viešą negaliojančią paleidimą () {doWork (s); }}; baseinas.vykdyti (r); }} static void doWork („Socket s“) {}}
Išvardinus 2 naudojimo būdus newFixedThreadPool (int)
gauti „thread pool“ vykdytoją, kuris pakartotinai naudoja penkias gijas. Tai taip pat pakeičia nauja gija (r) .start ();
su baseinas.vykdyti (r);
vykdyti vykdomas užduotis per bet kurią iš šių gijų.
3 sąraše pateikiamas dar vienas pavyzdys, kai programa skaito savavališko tinklalapio turinį. Jis pateikia gautas eilutes arba klaidos pranešimą, jei turinys nepasiekiamas ne ilgiau kaip penkias sekundes.