Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lära Executors och Trådpool | Hög-Nivå Synkroniseringsmekanismer
Multitrådning i Java

bookExecutors och Trådpool

Vi har redan utforskat en mängd olika mekanismer för att stödja multitrådning, och Executors är en av dem!

Vad är Executors och trådpooler?

Executors är en mekanism som erbjuder hög-nivå-abstraktioner för hantering av trådar. Det möjliggör att skapa och hantera en trådpool, som består av en uppsättning förskapade trådar som är redo att utföra uppgifter. Istället för att skapa en ny tråd för varje uppgift, skickas uppgifterna till poolen där deras exekvering fördelas mellan trådarna.

Så, vad är egentligen en trådpool? Det är en samling av förskapade trådar som är redo att utföra uppgifter. Genom att använda en trådpool undviks överkostnaden av att skapa och förstöra trådar upprepade gånger, eftersom samma trådar kan återanvändas för flera uppgifter.

Note
Notera

Om det finns fler uppgifter än trådar, väntar uppgifterna i Task Queue. En uppgift från kön hanteras av en tillgänglig tråd från poolen, och när uppgiften är klar tar tråden upp en ny uppgift från kön. När alla uppgifter i kön är slutförda, förblir trådarna aktiva och väntar på nya uppgifter.

Exempel från verkligheten

Tänk på en restaurang där kockar (trådar) förbereder beställningar (uppgifter). Istället för att anställa en ny kock för varje beställning, har restaurangen ett begränsat antal kockar som hanterar beställningar när de kommer in. När en kock är klar med en beställning tar denne nästa, vilket bidrar till att använda restaurangens resurser effektivt.

Main Method

newFixedThreadPool(int n): Skapar en pool med ett fast antal trådar lika med n.

Main.java

Main.java

copy
1
ExecutorService executorService = Executors.newFixedThreadPool(20);

newCachedThreadPool(): Skapar en pool som kan skapa nya trådar vid behov, men återanvänder tillgängliga trådar om sådana finns.

Main.java

Main.java

copy
1
ExecutorService executorService = Executors.newCachedThreadPool();

newSingleThreadExecutor(): Skapar en enkel trådpool som säkerställer att uppgifter exekveras sekventiellt, det vill säga en efter en. Detta är användbart för uppgifter som måste utföras i strikt ordning.

Main.java

Main.java

copy
1
ExecutorService executorService = Executors.newSingleThreadExecutor();

I alla exempel returnerar metoderna i Executors en implementation av gränssnittet ExecutorService, som används för att hantera trådar.

ExecutorService tillhandahåller metoder för att hantera en trådpool. Till exempel accepterar submit(Runnable task) en uppgift som ett Runnable-objekt och placerar den i en kö för exekvering. Den returnerar ett Future-objekt, vilket kan användas för att kontrollera statusen för uppgiften och erhålla ett resultat om uppgiften producerar ett resultat.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334
package com.example; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) { // Create a thread pool with 5 threads ExecutorService executor = Executors.newFixedThreadPool(5); // Define the task to be executed Runnable task = () -> { System.out.println("Task is running: " + Thread.currentThread().getName()); try { Thread.sleep(2000); // Simulate some work } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("Task completed: " + Thread.currentThread().getName()); }; // Submit the task for execution and get a `Future` Future<?> future = executor.submit(task); // Check if the task is done System.out.println("Is task done? " + future.isDone()); // You can use `future` to check the status of the task or wait for its completion // Example: future.get() - blocks until the task is completed (not used in this example) // Initiate an orderly shutdown of the executor service executor.shutdown(); } }

Metoden shutdown() påbörjar en kontrollerad avstängning av trådpoolen. Den slutar acceptera nya uppgifter men kommer att slutföra de aktuella uppgifterna. När du anropar denna metod kan poolen inte startas om.

Metoden awaitTermination(long timeout, TimeUnit unit) väntar på att alla uppgifter i poolen ska slutföras inom den angivna tidsramen. Detta är en blockerande väntan som gör det möjligt att säkerställa att alla uppgifter är slutförda innan poolen avslutas.

Vi har heller inte nämnt det huvudsakliga gränssnittet som hjälper till att spåra trådens tillstånd, nämligen Future-gränssnittet. Metoden submit() i ExecutorService-gränssnittet returnerar en implementation av Future-gränssnittet.

Om du vill hämta resultatet av trådens exekvering kan du använda metoden get(). Om tråden implementerar Runnable returnerar get()-metoden inget, men om det är Callable<T> returnerar den typen T.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031
package com.example; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(3); // Callable task that returns a result Callable<String> task = () -> { Thread.sleep(1000); // Simulate some work return "Task result"; }; Future<String> future = executor.submit(task); try { // Get the result of the task String result = future.get(); System.out.println("Task completed with result: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } executor.shutdown(); } }

Du kan också använda metoden cancel(boolean mayInterruptIfRunning) för att försöka avbryta exekveringen av en uppgift. Om uppgiften inte har startat än, kommer den att avbrytas. Om uppgiften redan körs kan den avbrytas beroende på flaggan mayInterruptIfRunning.

true: Om uppgiften körs kommer den att avbrytas genom att Thread.interrupt() anropas på den exekverande tråden. false: Om uppgiften körs kommer den inte att avbrytas, och avbrytningsförsöket kommer inte att påverka den pågående uppgiften.

Två metoder som intuitivt förklarar vad de gör:

  • isCancelled(): Kontrollerar om uppgiften har avbrutits;
  • isDone(): Kontrollerar om uppgiften har slutförts.

Exempel på användning

Note
Notering

Storleken på trådpoolen beror på typen av uppgifter som exekveras. Vanligtvis bör trådpoolens storlek inte vara hårdkodad; istället bör den vara anpassningsbar. Den optimala storleken bestäms genom att övervaka genomströmningen för de uppgifter som exekveras.

Det är maximalt effektivt att använda antalet threads = processor cores. Detta kan ses i koden med Runtime.getRuntime().availableProcessors().

Main.java

Main.java

copy
1
int availableProcessors = Runtime.getRuntime().availableProcessors();

Skillnader mellan att skapa trådar direkt och att använda ExecutorService

De huvudsakliga skillnaderna mellan att skapa trådar direkt och att använda ExecutorService är bekvämlighet och resurs­hantering. Manuell skapande av trådar kräver att varje tråd hanteras individuellt, vilket komplicerar koden och administrationen.

ExecutorService förenklar hanteringen genom att använda en trådpool, vilket gör uppgifts­hanteringen enklare. Dessutom kan manuell trådskapande leda till hög resursförbrukning, medan ExecutorService gör det möjligt att anpassa storleken på trådpoolen.

Var allt tydligt?

Hur kan vi förbättra det?

Tack för dina kommentarer!

Avsnitt 3. Kapitel 6

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 more about how ExecutorService manages threads?

What are some best practices for using thread pools?

How does the Future interface help in managing task results?

Awesome!

Completion rate improved to 3.33

bookExecutors och Trådpool

Svep för att visa menyn

Vi har redan utforskat en mängd olika mekanismer för att stödja multitrådning, och Executors är en av dem!

Vad är Executors och trådpooler?

Executors är en mekanism som erbjuder hög-nivå-abstraktioner för hantering av trådar. Det möjliggör att skapa och hantera en trådpool, som består av en uppsättning förskapade trådar som är redo att utföra uppgifter. Istället för att skapa en ny tråd för varje uppgift, skickas uppgifterna till poolen där deras exekvering fördelas mellan trådarna.

Så, vad är egentligen en trådpool? Det är en samling av förskapade trådar som är redo att utföra uppgifter. Genom att använda en trådpool undviks överkostnaden av att skapa och förstöra trådar upprepade gånger, eftersom samma trådar kan återanvändas för flera uppgifter.

Note
Notera

Om det finns fler uppgifter än trådar, väntar uppgifterna i Task Queue. En uppgift från kön hanteras av en tillgänglig tråd från poolen, och när uppgiften är klar tar tråden upp en ny uppgift från kön. När alla uppgifter i kön är slutförda, förblir trådarna aktiva och väntar på nya uppgifter.

Exempel från verkligheten

Tänk på en restaurang där kockar (trådar) förbereder beställningar (uppgifter). Istället för att anställa en ny kock för varje beställning, har restaurangen ett begränsat antal kockar som hanterar beställningar när de kommer in. När en kock är klar med en beställning tar denne nästa, vilket bidrar till att använda restaurangens resurser effektivt.

Main Method

newFixedThreadPool(int n): Skapar en pool med ett fast antal trådar lika med n.

Main.java

Main.java

copy
1
ExecutorService executorService = Executors.newFixedThreadPool(20);

newCachedThreadPool(): Skapar en pool som kan skapa nya trådar vid behov, men återanvänder tillgängliga trådar om sådana finns.

Main.java

Main.java

copy
1
ExecutorService executorService = Executors.newCachedThreadPool();

newSingleThreadExecutor(): Skapar en enkel trådpool som säkerställer att uppgifter exekveras sekventiellt, det vill säga en efter en. Detta är användbart för uppgifter som måste utföras i strikt ordning.

Main.java

Main.java

copy
1
ExecutorService executorService = Executors.newSingleThreadExecutor();

I alla exempel returnerar metoderna i Executors en implementation av gränssnittet ExecutorService, som används för att hantera trådar.

ExecutorService tillhandahåller metoder för att hantera en trådpool. Till exempel accepterar submit(Runnable task) en uppgift som ett Runnable-objekt och placerar den i en kö för exekvering. Den returnerar ett Future-objekt, vilket kan användas för att kontrollera statusen för uppgiften och erhålla ett resultat om uppgiften producerar ett resultat.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334
package com.example; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) { // Create a thread pool with 5 threads ExecutorService executor = Executors.newFixedThreadPool(5); // Define the task to be executed Runnable task = () -> { System.out.println("Task is running: " + Thread.currentThread().getName()); try { Thread.sleep(2000); // Simulate some work } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("Task completed: " + Thread.currentThread().getName()); }; // Submit the task for execution and get a `Future` Future<?> future = executor.submit(task); // Check if the task is done System.out.println("Is task done? " + future.isDone()); // You can use `future` to check the status of the task or wait for its completion // Example: future.get() - blocks until the task is completed (not used in this example) // Initiate an orderly shutdown of the executor service executor.shutdown(); } }

Metoden shutdown() påbörjar en kontrollerad avstängning av trådpoolen. Den slutar acceptera nya uppgifter men kommer att slutföra de aktuella uppgifterna. När du anropar denna metod kan poolen inte startas om.

Metoden awaitTermination(long timeout, TimeUnit unit) väntar på att alla uppgifter i poolen ska slutföras inom den angivna tidsramen. Detta är en blockerande väntan som gör det möjligt att säkerställa att alla uppgifter är slutförda innan poolen avslutas.

Vi har heller inte nämnt det huvudsakliga gränssnittet som hjälper till att spåra trådens tillstånd, nämligen Future-gränssnittet. Metoden submit() i ExecutorService-gränssnittet returnerar en implementation av Future-gränssnittet.

Om du vill hämta resultatet av trådens exekvering kan du använda metoden get(). Om tråden implementerar Runnable returnerar get()-metoden inget, men om det är Callable<T> returnerar den typen T.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031
package com.example; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(3); // Callable task that returns a result Callable<String> task = () -> { Thread.sleep(1000); // Simulate some work return "Task result"; }; Future<String> future = executor.submit(task); try { // Get the result of the task String result = future.get(); System.out.println("Task completed with result: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } executor.shutdown(); } }

Du kan också använda metoden cancel(boolean mayInterruptIfRunning) för att försöka avbryta exekveringen av en uppgift. Om uppgiften inte har startat än, kommer den att avbrytas. Om uppgiften redan körs kan den avbrytas beroende på flaggan mayInterruptIfRunning.

true: Om uppgiften körs kommer den att avbrytas genom att Thread.interrupt() anropas på den exekverande tråden. false: Om uppgiften körs kommer den inte att avbrytas, och avbrytningsförsöket kommer inte att påverka den pågående uppgiften.

Två metoder som intuitivt förklarar vad de gör:

  • isCancelled(): Kontrollerar om uppgiften har avbrutits;
  • isDone(): Kontrollerar om uppgiften har slutförts.

Exempel på användning

Note
Notering

Storleken på trådpoolen beror på typen av uppgifter som exekveras. Vanligtvis bör trådpoolens storlek inte vara hårdkodad; istället bör den vara anpassningsbar. Den optimala storleken bestäms genom att övervaka genomströmningen för de uppgifter som exekveras.

Det är maximalt effektivt att använda antalet threads = processor cores. Detta kan ses i koden med Runtime.getRuntime().availableProcessors().

Main.java

Main.java

copy
1
int availableProcessors = Runtime.getRuntime().availableProcessors();

Skillnader mellan att skapa trådar direkt och att använda ExecutorService

De huvudsakliga skillnaderna mellan att skapa trådar direkt och att använda ExecutorService är bekvämlighet och resurs­hantering. Manuell skapande av trådar kräver att varje tråd hanteras individuellt, vilket komplicerar koden och administrationen.

ExecutorService förenklar hanteringen genom att använda en trådpool, vilket gör uppgifts­hanteringen enklare. Dessutom kan manuell trådskapande leda till hög resursförbrukning, medan ExecutorService gör det möjligt att anpassa storleken på trådpoolen.

Var allt tydligt?

Hur kan vi förbättra det?

Tack för dina kommentarer!

Avsnitt 3. Kapitel 6
some-alt