Programavimas

„Java 101“: „Java“ sutapimas be skausmo, 1 dalis

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 ()ir praneš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š ExecutorServicemetodai 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škiama vienetas vienetų, nurodytų „TimeUnit“ enum; pavyzdžiui, „TimeUnit“. SEKUNDĖS. Šis metimas meta java.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 a Ateitis 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, Skambinamair 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.

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