Forkjoinpool
Klassen ForkJoinPool i Java for arbeid med Fork/Join-rammeverket er nettopp realiseringen av dette. Den gir mekanismer for å håndtere oppgaver som kan deles opp i mindre deloppgaver og utføres parallelt.
Main.java
1ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinPool er basert på konseptet med to grunnleggende handlinger: fork og join. Fork er en handling der en stor oppgave deles opp i flere mindre deloppgaver som kan utføres parallelt. Join er prosessen der resultatene fra disse deloppgavene kombineres for å danne resultatet av den opprinnelige oppgaven.
Casestudie
Tenk deg at du må analysere et enormt datasett som kan deles opp i flere mindre sett. Hvis hvert datasett behandles separat og resultatene deretter slås sammen, kan behandlingen akselereres betydelig, spesielt på multiprosessorsystemer.
Hvordan bruke ForkJoinPool
Det finnes 2 klasser for å implementere oppgaver i dem, nemlig RecursiveTask og RecursiveAction. Begge krever at den abstrakte metoden compute() implementeres. I RecursiveTask returnerer compute()-metoden en verdi, mens i RecursiveAction returnerer compute()-metoden void.
Main.java
12345678class ExampleTask extends RecursiveTask<String> { @Override protected String compute() { // code return null; } }
Når vi arver fra en RecursiveTask må vi sørge for å spesifisere hvilken datatype vår compute()-metode skal returnere ved å bruke denne syntaksen RecursiveTask<String>.
Main.java
1234567class ExampleAction extends RecursiveAction { @Override protected void compute() { // code } }
Her er det motsatt, vi trenger ikke å spesifisere typen eksplisitt i RecursiveAction, fordi metoden vår ikke returnerer noe og kun utfører oppgaven.
Starte en oppgave
For øvrig kan en oppgave startes for utførelse uten å bruke ForkJoinPool, kun ved å bruke metodene fork() og join().
Det er viktig å merke seg at fork()-metoden sender oppgaven til en tråd. Metoden join() brukes for å hente resultatet.
Main.java
12345public static void main(String[] args) { ExampleTask simpleClass = new ExampleTask(); simpleClass.fork(); System.out.println(simpleClass.join()); }
ForkJoinPool hovedmetoder
invoke(): Starter en oppgave i poolen og venter til den er ferdig. Returnerer resultatet av oppgavens utførelse;submit(): Sender en oppgave til poolen, men blokkerer ikke den nåværende tråden mens den venter på at oppgaven skal fullføres. Kan brukes til å sende inn oppgaver og hente deresFuture-objekter;execute(): Kjører en oppgave i poolen, men returnerer ikke et resultat og blokkerer ikke den nåværende tråden.
Hvordan kan vi starte en oppgave ved å bruke ForkJoinPool, veldig enkelt!
Main.java
12345public static void main(String[] args) { ExampleAction simpleClass = new ExampleAction(); ForkJoinPool forkJoinPool = new ForkJoinPool(); System.out.println(forkJoinPool.invoke(simpleClass)); }
Men hva er forskjellen mellom å kjøre fork(), join() og via ForkJoinPool -klassen?
Det er veldig enkelt! Når du bruker den første metoden med hjelp av fork(), join()-metodene, starter vi oppgaven i den samme tråden hvor disse metodene ble kalt, og blokkerer denne tråden, mens vi med hjelp av ForkJoinPool-klassen får tildelt en tråd fra poolen og den kjører i denne tråden uten å blokkere hovedtråden.
La oss se nærmere på dette, anta at vi har en slik implementasjon:
Main.java
12345678class ExampleRecursiveTask extends RecursiveTask<String> { @Override protected String compute() { System.out.println("Thread: " + Thread.currentThread().getName()); return "Wow, it works!!!"; } }
Og vi ønsker å kjøre denne koden ved hjelp av fork() og join(). La oss se hva som vil bli skrevet ut til konsollen og hvilken tråd som vil utføre denne oppgaven.
Main.java
1234567public class Main { public static void main(String[] args) { ExampleRecursiveTask simpleClass = new ExampleRecursiveTask(); simpleClass.fork(); System.out.println(simpleClass.join()); } }
Og vi får denne utdataen i konsollen:
Thread: main
Wow, it works!!!
La oss nå se hva som skjer hvis vi kjører med ForkJoinPool:
Main.java
1234567public class Main { public static void main(String[] args) { ExampleRecursiveTask simpleClass = new ExampleRecursiveTask(); ForkJoinPool forkJoinPool = new ForkJoinPool(); System.out.println(forkJoinPool.invoke(simpleClass)); } }
Og vi får denne konklusjonen:
Thread: ForkJoinPool-1-worker-1
Wow, it works!!!
Som du kan se er forskjellen at ved bruk av første metode kjøres oppgaven i samme tråd som kaller denne oppgaven (Main thread). Men hvis vi bruker andre metode, hentes tråden fra ForkJoinPool-trådpoolen og hovedtråden som kalte denne logikken blir ikke blokkert og fortsetter!
Hvordan implementere Fork/Join i kode
Den enkleste måten å forklare dette på ville vært i en video, i stedet for å gi deg 50-80 linjer med kode og forklare det punkt for punkt.
ForkJoinPool er effektiv for oppgaver som lett kan deles opp i mindre deloppgaver. Dersom oppgavene er for små, kan bruk av ForkJoinPool imidlertid ikke gi noen vesentlig ytelsesgevinst.
Takk for tilbakemeldingene dine!
Spør AI
Spør AI
Spør om hva du vil, eller prøv ett av de foreslåtte spørsmålene for å starte chatten vår
Can you explain the difference between RecursiveTask and RecursiveAction again?
How do I decide what threshold to use when splitting tasks?
Can you give a simple code example of using ForkJoinPool?
Awesome!
Completion rate improved to 3.33
Forkjoinpool
Sveip for å vise menyen
Klassen ForkJoinPool i Java for arbeid med Fork/Join-rammeverket er nettopp realiseringen av dette. Den gir mekanismer for å håndtere oppgaver som kan deles opp i mindre deloppgaver og utføres parallelt.
Main.java
1ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinPool er basert på konseptet med to grunnleggende handlinger: fork og join. Fork er en handling der en stor oppgave deles opp i flere mindre deloppgaver som kan utføres parallelt. Join er prosessen der resultatene fra disse deloppgavene kombineres for å danne resultatet av den opprinnelige oppgaven.
Casestudie
Tenk deg at du må analysere et enormt datasett som kan deles opp i flere mindre sett. Hvis hvert datasett behandles separat og resultatene deretter slås sammen, kan behandlingen akselereres betydelig, spesielt på multiprosessorsystemer.
Hvordan bruke ForkJoinPool
Det finnes 2 klasser for å implementere oppgaver i dem, nemlig RecursiveTask og RecursiveAction. Begge krever at den abstrakte metoden compute() implementeres. I RecursiveTask returnerer compute()-metoden en verdi, mens i RecursiveAction returnerer compute()-metoden void.
Main.java
12345678class ExampleTask extends RecursiveTask<String> { @Override protected String compute() { // code return null; } }
Når vi arver fra en RecursiveTask må vi sørge for å spesifisere hvilken datatype vår compute()-metode skal returnere ved å bruke denne syntaksen RecursiveTask<String>.
Main.java
1234567class ExampleAction extends RecursiveAction { @Override protected void compute() { // code } }
Her er det motsatt, vi trenger ikke å spesifisere typen eksplisitt i RecursiveAction, fordi metoden vår ikke returnerer noe og kun utfører oppgaven.
Starte en oppgave
For øvrig kan en oppgave startes for utførelse uten å bruke ForkJoinPool, kun ved å bruke metodene fork() og join().
Det er viktig å merke seg at fork()-metoden sender oppgaven til en tråd. Metoden join() brukes for å hente resultatet.
Main.java
12345public static void main(String[] args) { ExampleTask simpleClass = new ExampleTask(); simpleClass.fork(); System.out.println(simpleClass.join()); }
ForkJoinPool hovedmetoder
invoke(): Starter en oppgave i poolen og venter til den er ferdig. Returnerer resultatet av oppgavens utførelse;submit(): Sender en oppgave til poolen, men blokkerer ikke den nåværende tråden mens den venter på at oppgaven skal fullføres. Kan brukes til å sende inn oppgaver og hente deresFuture-objekter;execute(): Kjører en oppgave i poolen, men returnerer ikke et resultat og blokkerer ikke den nåværende tråden.
Hvordan kan vi starte en oppgave ved å bruke ForkJoinPool, veldig enkelt!
Main.java
12345public static void main(String[] args) { ExampleAction simpleClass = new ExampleAction(); ForkJoinPool forkJoinPool = new ForkJoinPool(); System.out.println(forkJoinPool.invoke(simpleClass)); }
Men hva er forskjellen mellom å kjøre fork(), join() og via ForkJoinPool -klassen?
Det er veldig enkelt! Når du bruker den første metoden med hjelp av fork(), join()-metodene, starter vi oppgaven i den samme tråden hvor disse metodene ble kalt, og blokkerer denne tråden, mens vi med hjelp av ForkJoinPool-klassen får tildelt en tråd fra poolen og den kjører i denne tråden uten å blokkere hovedtråden.
La oss se nærmere på dette, anta at vi har en slik implementasjon:
Main.java
12345678class ExampleRecursiveTask extends RecursiveTask<String> { @Override protected String compute() { System.out.println("Thread: " + Thread.currentThread().getName()); return "Wow, it works!!!"; } }
Og vi ønsker å kjøre denne koden ved hjelp av fork() og join(). La oss se hva som vil bli skrevet ut til konsollen og hvilken tråd som vil utføre denne oppgaven.
Main.java
1234567public class Main { public static void main(String[] args) { ExampleRecursiveTask simpleClass = new ExampleRecursiveTask(); simpleClass.fork(); System.out.println(simpleClass.join()); } }
Og vi får denne utdataen i konsollen:
Thread: main
Wow, it works!!!
La oss nå se hva som skjer hvis vi kjører med ForkJoinPool:
Main.java
1234567public class Main { public static void main(String[] args) { ExampleRecursiveTask simpleClass = new ExampleRecursiveTask(); ForkJoinPool forkJoinPool = new ForkJoinPool(); System.out.println(forkJoinPool.invoke(simpleClass)); } }
Og vi får denne konklusjonen:
Thread: ForkJoinPool-1-worker-1
Wow, it works!!!
Som du kan se er forskjellen at ved bruk av første metode kjøres oppgaven i samme tråd som kaller denne oppgaven (Main thread). Men hvis vi bruker andre metode, hentes tråden fra ForkJoinPool-trådpoolen og hovedtråden som kalte denne logikken blir ikke blokkert og fortsetter!
Hvordan implementere Fork/Join i kode
Den enkleste måten å forklare dette på ville vært i en video, i stedet for å gi deg 50-80 linjer med kode og forklare det punkt for punkt.
ForkJoinPool er effektiv for oppgaver som lett kan deles opp i mindre deloppgaver. Dersom oppgavene er for små, kan bruk av ForkJoinPool imidlertid ikke gi noen vesentlig ytelsesgevinst.
Takk for tilbakemeldingene dine!