Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lære Forkjoinpool | Beste Praksiser for Multitråding
Quizzes & Challenges
Quizzes
Challenges
/
Multitråding i Java

bookForkjoinpool

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

Main.java

copy
1
ForkJoinPool 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

Main.java

copy
12345678
class 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

Main.java

copy
1234567
class 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

Main.java

copy
12345
public 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 deres Future-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

Main.java

copy
12345
public 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

Main.java

copy
12345678
class 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

Main.java

copy
1234567
public 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

Main.java

copy
1234567
public 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.

Note
Merk

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.

Alt var klart?

Hvordan kan vi forbedre det?

Takk for tilbakemeldingene dine!

Seksjon 4. Kapittel 2

Spør AI

expand

Spør AI

ChatGPT

Spør om hva du vil, eller prøv ett av de foreslåtte spørsmålene for å starte chatten vår

Suggested prompts:

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

bookForkjoinpool

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

Main.java

copy
1
ForkJoinPool 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

Main.java

copy
12345678
class 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

Main.java

copy
1234567
class 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

Main.java

copy
12345
public 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 deres Future-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

Main.java

copy
12345
public 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

Main.java

copy
12345678
class 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

Main.java

copy
1234567
public 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

Main.java

copy
1234567
public 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.

Note
Merk

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.

Alt var klart?

Hvordan kan vi forbedre det?

Takk for tilbakemeldingene dine!

Seksjon 4. Kapittel 2
some-alt