Vodič za krađu posla u Javi

1. Pregled

U ovom uputstvu pogledat ćemo koncept krađe djela u Javi.

2. Što je krađa posla?

Krađa posla uvedena je u Javi s ciljem smanjenje sukoba u višenitnim aplikacijama. To se radi pomoću okvira fork / join.

2.1. Pristup podijeli i osvoji

U okviru fork / join, problemi ili zadaci rekurzivno se raščlanjuju na pod-zadatke. Podzadaci se zatim rješavaju pojedinačno, a podrezultati se kombiniraju da bi se dobio rezultat:

Rješavanje rezultata (problem problema) {ako (problem je mali) izravno rješavanje problema ostalo {podijeliti problem na neovisne dijelove rastaviti nove podzadate za rješavanje svakog dijela pridružiti se svim podzadacima sastaviti rezultat iz podrezultata}}

2.2. Radničke niti

Razbijeni zadatak rješava se uz pomoć radničkih niti koje osigurava spremište niti. Svaka radnička nit imat će pod-zadatke za koje je odgovorna. Oni se pohranjuju u dvostruke redove (deques).

Svaka radna nit dobiva pod-zadatke iz svog deque-a kontinuiranim iskakanjem pod-zadatka s vrha deque-a. Kad je deque radničke niti prazan, to znači da su svi podzadaci iskočeni i dovršeni.

U ovom trenutku radnička nit nasumično odabire ravnopravnu nit spremišta niti iz koje može "ukrasti" rad. Zatim koristi pristup prvi ulazak, prvi izlazak (FIFO) za preuzimanje pod-zadataka s repnog dijela žrtvine deke.

3. Provedba okvira za račvanje / pridruživanje

Možemo stvoriti spremište niti za krađu rada koristeći bilo ForkJoinPool razred ili Izvršitelji razred:

ForkJoinPool commonPool = ForkJoinPool.commonPool (); ExecutorService workStealingPool = Izvršitelji.newWorkStealingPool ();

The Izvršitelji razred ima preopterećen newWorkStealingPool metoda koja uzima cjelobrojni argument koji predstavlja razina paralelizma.

Izvršitelji.newWorkStealingPool je apstrakcija od ForkJoinPool.commonPool. Jedina je razlika u tome Izvršitelji.newWorkStealingPool stvara bazen u asinkronom načinu i ForkJoinPool.commonPool nema.

4. Sinkroni vs Asinhroni bazeni navoja

ForkJoinPool.commonPool koristi konfiguraciju reda za zadnji ulaz, prvi izlaz (LIFO), dok Izvršitelji.newWorkStealingPool koristi pristup prvi-prvi-prvi (FIFO).

Prema Dougu Leau, FIFO pristup ima sljedeće prednosti u odnosu na LIFO:

  • Smanjuje svađu tako što krađe djeluju na suprotnoj strani dequea kao vlasnici
  • Iskorištava svojstvo rekurzivnih algoritama podijeli-osvoji i rano generiraj "velike" zadatke

Druga gornja točka znači da je moguće stariji ukradeni zadatak dodatno rastaviti pomoću niti koja ga je ukrala.

Prema postavkama Java dokumentacije asyncMode do pravi može biti prikladan za upotrebu sa zadacima u stilu događaja koji se nikada ne pridružuju.

5. Radni primjer - Pronalaženje prostih brojeva

Upotrijebit ćemo primjer pronalaska prostih brojeva iz zbirke brojeva kako bismo prikazali prednosti računanja vremena okvira za krađu rada. Pokazat ćemo i razlike između korištenja sinkronog i asinkronog spremišta niti.

5.1. Problem prostih brojeva

Pronalaženje prostih brojeva iz zbirke brojeva može biti računski skup postupak. To je uglavnom zbog veličine zbirke brojeva.

The Primarni brojevi klasa nam pomaže pronaći jednostavne brojeve:

javna klasa PrimeNumbers proširuje RecursiveAction {private int lowerBound; private int upperBound; privatnost int granularnosti; statički konačni popis GRANULARNOSTI = Arrays.asList (1, 10, 100, 1000, 10000); privatni AtomicInteger noOfPrimeNumbers; PrimeNumbers (int lowerBound, int upperBound, int granularity, AtomicInteger noOfPrimeNumbers) {this.lowerBound = lowerBound; this.upperBound = upperBound; this.granularity = granularnost; this.noOfPrimeNumbers = noOfPrimeNumbers; } // ostali konstruktori i metode private List subTasks () {List subTasks = new ArrayList (); for (int i = 1; i <= this.upperBound / granularity; i ++) {int upper = i * granularnost; int donji = (gornji - granularnost) + 1; subTasks.add (novi PrimeNumbers (donji, gornji, noOfPrimeNumbers)); } vratiti podzadatke; } @Override protected void compute () {if (((upperBound + 1) - lowerBound)> granularnost) {ForkJoinTask.invokeAll (subTasks ()); } else {findPrimeNumbers (); }} void findPrimeNumbers () {for (int num = lowerBound; num <= upperBound; num ++) {if (isPrime (num)) {noOfPrimeNumbers.getAndIncrement (); }}} public int noOfPrimeNumbers () {return noOfPrimeNumbers.intValue (); }}

Nekoliko važnih stvari koje treba imati na umu o ovoj klasi:

  • Proširuje se Rekurzivno djelovanje, koji nam omogućuje implementaciju izračunati metoda koja se koristi u računskim zadacima pomoću spremišta niti
  • Rekurzivno raščlanjuje zadatke na podzadatke temeljene na granularnost vrijednost
  • Konstruktori uzimaju niži i Gornji vezane vrijednosti koje kontroliraju raspon brojeva kojima želimo odrediti proste brojeve
  • Omogućuje nam određivanje prostih brojeva pomoću bilo kojeg skupa niti za krađu posla ili jedne niti

5.2. Brže rješavanje problema s nitima

Utvrdimo proste brojeve na jedan navoj i također pomoću spremišta niti krađe rada.

Prvo, da vidimo jednonitni pristup:

PrimeNumbers primes = novi PrimeNumbers (10000); primes.findPrimeNumbers ();

A sada, ForkJoinPool.commonPool pristup:

PrimeNumbers primes = novi PrimeNumbers (10000); ForkJoinPool pool = ForkJoinPool.commonPool (); pool.invoke (prosti brojevi); pool.shutdown ();

Napokon ćemo pogledati Izvršitelji.newWorkStealingPool pristup:

PrimeNumbers prosti brojevi = novi PrimeNumbers (10000); int paralelizam = ForkJoinPool.getCommonPoolParallelism (); ForkJoinPool stealer = (ForkJoinPool) Izvršitelji.newWorkStealingPool (paralelizam); stealer.invoke (prosti brojevi); stealer.shutdown ();

Koristimo prizivati metoda ForkJoinPool klasa za prosljeđivanje zadataka spremištu niti. Ova metoda uzima u obzir primjere podklasa Rekurzivno djelovanje. Koristeći Java Microbench Harness, uspoređujemo ove različite pristupe jedni s drugima u smislu prosječnog vremena po operaciji:

# Trčanje dovršeno. Ukupno vrijeme: 00:04:50 Benchmark Mode Cnt Score Greške Jedinice PrimeNumbersUnitTest.Benchmarker.commonPoolBenchmark avgt 20 119.885 ± 9.917 ms / op PrimeNumbersUnitTest.Benchmarker.newWorkStealingPoolBenchmark avgt 20 119.791 ± 7.811 ms.9 opT. ms / op

Jasno je da oboje ForkJoinPool.commonPool i Izvršitelji.newWorkStealingPool omogućuju nam brže određivanje prostih brojeva nego kod jednonitnog pristupa.

Okvir fork / join spremišta omogućuje nam da raščlanimo zadatak na pod-zadatke. Zbirku od 10.000 cijelih brojeva podijelili smo u serije od 1-100, 101-200, 201-300 i tako dalje. Zatim smo odredili proste brojeve za svaku seriju i učinili ukupan broj prostih brojeva dostupnim s našim noOfPrimeNumbers metoda.

5.3. Krađa posla za računanje

Sa sinkronim spremištem niti, ForkJoinPool.commonPool stavlja niti u spremište sve dok je zadatak još u tijeku.Kao rezultat toga, razina krađe posla ne ovisi o razini detaljnosti zadatka.

Asinkroni Izvršitelji.newWorkStealingPoolviše se upravlja, omogućavajući da razina krađe posla ovisi o razini granularnosti zadatka.

Dobivamo razinu krađe posla koristeći getStealCount od ForkJoinPool razred:

dugo krade = forkJoinPool.getStealCount ();

Utvrđivanje broja krađa posla za Izvršitelji.newWorkStealingPool i ForkJoinPool.commonPool daje nam različito ponašanje:

Izvršitelji.newWorkStealingPool -> Granulacija: [1], Ukrade: [6564] Granulacija: [10], Ukrade: [572] Granulacija: [100], Ukrade: [56] Granulacija: [1000], Ukrade: [60] Granulacija : [10000], ukrade: [1] ForkJoinPool.commonPool -> granulacija: [1], krađa: [6923] granulacija: [10], krađa: [7540] granulacija: [100], krađa: [7605] granulacija: [1000], ukrade: [7681] granularnost: [10000], ukrade: [7681]

Kada se granularnost promijeni iz fine u grubu (1 do 10 000) za Izvršitelji.newWorkStealingPool, razina krađe posla se smanjuje. Stoga je broj ukradenih slučajeva onaj kada se zadatak ne raščlani (granularnost od 10.000).

The ForkJoinPool.commonPool ima drugačije ponašanje. Razina krađe posla uvijek je visoka i na nju ne utječe puno promjena u granulaciji zadataka.

Tehnički gledano, naš primjer jednostavnih brojeva je onaj koji podržava asinkronu obradu zadataka u stilu događaja. To je zato što naša implementacija ne nameće spajanje rezultata.

Može se napraviti slučaj Izvršitelji.newWorkStealingPool nudi najbolje korištenje resursa u rješavanju problema.

6. Zaključak

U ovom smo članku pogledali krađu posla i kako je primijeniti pomoću okvira fork / join. Također smo pogledali primjere krađe posla i kako to može poboljšati vrijeme obrade i upotrebu resursa.

Kao i uvijek, puni izvorni kod primjera dostupan je na GitHub-u.


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