Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lära Forkjoinpool | Bästa Praxis För Multitrådning
Quizzes & Challenges
Quizzes
Challenges
/
Multitrådning i Java

bookForkjoinpool

Klassen ForkJoinPool i Java för arbete med Fork/Join-ramverket är just detta. Den tillhandahåller mekanismer för att hantera uppgifter som kan delas upp i mindre deluppgifter och köras parallellt.

Main.java

Main.java

copy
1
ForkJoinPool forkJoinPool = new ForkJoinPool();

ForkJoinPool bygger på konceptet av två grundläggande åtgärder: fork och join. Fork är en åtgärd där en stor uppgift delas upp i flera mindre deluppgifter som kan köras parallellt. Join är processen där resultaten från dessa deluppgifter kombineras för att bilda resultatet av den ursprungliga uppgiften.

Fallstudie

Föreställ dig att du behöver analysera en enorm datamängd som kan delas upp i flera mindre mängder. Om du behandlar varje datamängd separat och sedan slår ihop resultaten kan du snabba upp bearbetningen avsevärt, särskilt på multiprocessorsystem.

Hur man använder ForkJoinPool

Det finns 2 klasser för att implementera uppgifter i dem, de är RecursiveTask och RecursiveAction. Båda kräver att en abstrakt metod compute() implementeras. I RecursiveTask returnerar metoden compute() ett värde, medan i RecursiveAction returnerar metoden compute() void.

Main.java

Main.java

copy
12345678
class ExampleTask extends RecursiveTask<String> { @Override protected String compute() { // code return null; } }

När vi ärver från en RecursiveTask måste vi ange vilken datatyp som vår compute()-metod kommer att returnera genom att använda denna syntax RecursiveTask<String>.

Main.java

Main.java

copy
1234567
class ExampleAction extends RecursiveAction { @Override protected void compute() { // code } }

Här är det tvärtom, vi behöver inte ange typen explicit i RecursiveAction, eftersom vår metod inte kommer att returnera något utan endast utföra uppgiften.

Starta en uppgift

För övrigt kan vi starta en uppgift för exekvering utan att ens använda ForkJoinPool, bara genom att använda metoderna fork() och join().

Det är viktigt att notera att metoden fork() skickar uppgiften till någon tråd. Metoden join() används för att hämta 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 huvudmetoder

  • invoke(): Startar en uppgift i poolen och väntar på att den ska slutföras. Returnerar resultatet av uppgiftens slutförande;
  • submit(): Skickar in en uppgift till poolen, men blockerar inte den aktuella tråden medan den väntar på att uppgiften ska slutföras. Kan användas för att skicka in uppgifter och hämta deras Future-objekt;
  • execute(): Kör en uppgift i poolen, men returnerar inget resultat och blockerar inte den aktuella tråden.

Hur kan vi starta en uppgift med hjälp av ForkJoinPool, väldigt 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 vad är skillnaden mellan att köra fork(), join() och via klassen ForkJoinPool?

Det är mycket enkelt! När du använder den första metoden med hjälp av fork(), join()-metoderna startar vi uppgiften i samma tråd där dessa metoder anropades, vilket blockerar denna tråd, medan vi med hjälp av klassen ForkJoinPool får en tråd tilldelad från poolen och den arbetar i denna tråd utan att blockera huvudtråden.

Låt oss titta närmare, anta att vi har en sådan implementation:

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!!!"; } }

Och vi vill köra denna kod med hjälp av fork() och join(). Låt oss se vad som kommer att skrivas ut i konsolen och vilken tråd som kommer att utföra denna uppgift.

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()); } }

Och vi får detta utdata till konsolen:

Thread: main
Wow, it works!!!

Låt oss nu undersöka vad som händer om vi kör 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)); } }

Och vi får denna slutsats:

Thread: ForkJoinPool-1-worker-1
Wow, it works!!!

Och som du kan se är skillnaden att när du använder första metoden så körs uppgiften i samma tråd som anropade denna uppgift (huvudtråden). Men om vi använder andra metoden, tas tråden från trådpoolen ForkJoinPool och huvudtråden som anropade denna logik blockeras inte utan fortsätter!

Hur man implementerar Fork/Join i kod

Det enklaste sättet att förklara detta vore genom en video, snarare än att ge dig 50-80 rader kod och förklara det punkt för punkt.

Note
Notering

ForkJoinPool är effektiv för uppgifter som enkelt kan delas upp i mindre deluppgifter. Om uppgifterna däremot är för små, kan användning av ForkJoinPool dock inte ge någon betydande prestandaförbättring.

Var allt tydligt?

Hur kan vi förbättra det?

Tack för dina kommentarer!

Avsnitt 4. Kapitel 2

Fråga AI

expand

Fråga AI

ChatGPT

Fråga vad du vill eller prova någon av de föreslagna frågorna för att starta vårt samtal

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

Svep för att visa menyn

Klassen ForkJoinPool i Java för arbete med Fork/Join-ramverket är just detta. Den tillhandahåller mekanismer för att hantera uppgifter som kan delas upp i mindre deluppgifter och köras parallellt.

Main.java

Main.java

copy
1
ForkJoinPool forkJoinPool = new ForkJoinPool();

ForkJoinPool bygger på konceptet av två grundläggande åtgärder: fork och join. Fork är en åtgärd där en stor uppgift delas upp i flera mindre deluppgifter som kan köras parallellt. Join är processen där resultaten från dessa deluppgifter kombineras för att bilda resultatet av den ursprungliga uppgiften.

Fallstudie

Föreställ dig att du behöver analysera en enorm datamängd som kan delas upp i flera mindre mängder. Om du behandlar varje datamängd separat och sedan slår ihop resultaten kan du snabba upp bearbetningen avsevärt, särskilt på multiprocessorsystem.

Hur man använder ForkJoinPool

Det finns 2 klasser för att implementera uppgifter i dem, de är RecursiveTask och RecursiveAction. Båda kräver att en abstrakt metod compute() implementeras. I RecursiveTask returnerar metoden compute() ett värde, medan i RecursiveAction returnerar metoden compute() void.

Main.java

Main.java

copy
12345678
class ExampleTask extends RecursiveTask<String> { @Override protected String compute() { // code return null; } }

När vi ärver från en RecursiveTask måste vi ange vilken datatyp som vår compute()-metod kommer att returnera genom att använda denna syntax RecursiveTask<String>.

Main.java

Main.java

copy
1234567
class ExampleAction extends RecursiveAction { @Override protected void compute() { // code } }

Här är det tvärtom, vi behöver inte ange typen explicit i RecursiveAction, eftersom vår metod inte kommer att returnera något utan endast utföra uppgiften.

Starta en uppgift

För övrigt kan vi starta en uppgift för exekvering utan att ens använda ForkJoinPool, bara genom att använda metoderna fork() och join().

Det är viktigt att notera att metoden fork() skickar uppgiften till någon tråd. Metoden join() används för att hämta 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 huvudmetoder

  • invoke(): Startar en uppgift i poolen och väntar på att den ska slutföras. Returnerar resultatet av uppgiftens slutförande;
  • submit(): Skickar in en uppgift till poolen, men blockerar inte den aktuella tråden medan den väntar på att uppgiften ska slutföras. Kan användas för att skicka in uppgifter och hämta deras Future-objekt;
  • execute(): Kör en uppgift i poolen, men returnerar inget resultat och blockerar inte den aktuella tråden.

Hur kan vi starta en uppgift med hjälp av ForkJoinPool, väldigt 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 vad är skillnaden mellan att köra fork(), join() och via klassen ForkJoinPool?

Det är mycket enkelt! När du använder den första metoden med hjälp av fork(), join()-metoderna startar vi uppgiften i samma tråd där dessa metoder anropades, vilket blockerar denna tråd, medan vi med hjälp av klassen ForkJoinPool får en tråd tilldelad från poolen och den arbetar i denna tråd utan att blockera huvudtråden.

Låt oss titta närmare, anta att vi har en sådan implementation:

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!!!"; } }

Och vi vill köra denna kod med hjälp av fork() och join(). Låt oss se vad som kommer att skrivas ut i konsolen och vilken tråd som kommer att utföra denna uppgift.

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()); } }

Och vi får detta utdata till konsolen:

Thread: main
Wow, it works!!!

Låt oss nu undersöka vad som händer om vi kör 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)); } }

Och vi får denna slutsats:

Thread: ForkJoinPool-1-worker-1
Wow, it works!!!

Och som du kan se är skillnaden att när du använder första metoden så körs uppgiften i samma tråd som anropade denna uppgift (huvudtråden). Men om vi använder andra metoden, tas tråden från trådpoolen ForkJoinPool och huvudtråden som anropade denna logik blockeras inte utan fortsätter!

Hur man implementerar Fork/Join i kod

Det enklaste sättet att förklara detta vore genom en video, snarare än att ge dig 50-80 rader kod och förklara det punkt för punkt.

Note
Notering

ForkJoinPool är effektiv för uppgifter som enkelt kan delas upp i mindre deluppgifter. Om uppgifterna däremot är för små, kan användning av ForkJoinPool dock inte ge någon betydande prestandaförbättring.

Var allt tydligt?

Hur kan vi förbättra det?

Tack för dina kommentarer!

Avsnitt 4. Kapitel 2
some-alt