Java Language
Executor, ExecutorService en Thread pools
Zoeken…
Invoering
De Executor- interface in Java biedt een manier om taakverzending te ontkoppelen van de mechanica van hoe elke taak wordt uitgevoerd, inclusief details van threadgebruik, planning, enz. Normaal wordt een Executor gebruikt in plaats van expliciet threads te maken. Met Executors hoeven ontwikkelaars hun code niet aanzienlijk te herschrijven om het taakuitvoeringsbeleid van hun programma gemakkelijk te kunnen afstemmen.
Opmerkingen
valkuilen
- Wanneer u een taak plant voor herhaalde uitvoering, afhankelijk van de gebruikte ScheduledExecutorService, kan uw taak worden opgeschort voor verdere uitvoering, als een uitvoering van uw taak een uitzondering veroorzaakt die niet wordt afgehandeld. Zie moeder F ** k de ScheduledExecutorService!
Vuur en vergeet - Uit te voeren taken
Uitvoerders accepteren een java.lang.Runnable
die (mogelijk rekenkundig of anderszins langlopende of zware) code bevat die in een andere thread moet worden uitgevoerd.
Gebruik zou zijn:
Executor exec = anExecutor;
exec.execute(new Runnable() {
@Override public void run() {
//offloaded work, no need to get result back
}
});
Merk op dat je met deze uitvoerder geen middelen hebt om enige berekende waarde terug te krijgen.
Met Java 8 kan men lambdas gebruiken om het codevoorbeeld in te korten.
Executor exec = anExecutor;
exec.execute(() -> {
//offloaded work, no need to get result back
});
ThreadPoolExecutor
Een veelgebruikte uitvoerder is de ThreadPoolExecutor
, die zorgt voor de verwerking van de threads. U kunt het minimale aantal threads configureren dat de uitvoerder altijd moet onderhouden als er niet veel te doen is (dit wordt kerngrootte genoemd) en een maximale threadgrootte waarnaar de pool kan groeien, als er meer werk is. Zodra de werklast afneemt, vermindert de pool langzaam het aantal threads opnieuw totdat het de minimale grootte bereikt.
ThreadPoolExecutor pool = new ThreadPoolExecutor(
1, // keep at least one thread ready,
// even if no Runnables are executed
5, // at most five Runnables/Threads
// executed in parallel
1, TimeUnit.MINUTES, // idle Threads terminated after one
// minute, when min Pool size exceeded
new ArrayBlockingQueue<Runnable>(10)); // outstanding Runnables are kept here
pool.execute(new Runnable() {
@Override public void run() {
//code to run
}
});
Opmerking Als u de ThreadPoolExecutor
configureert met een onbeperkte wachtrij, overschrijdt het corePoolSize
threads corePoolSize
omdat nieuwe threads alleen worden gemaakt als de wachtrij vol is:
ThreadPoolExecutor met alle parameters:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
van JavaDoc
Als er meer dan corePoolSize maar minder dan maximumPoolSize threads worden uitgevoerd, wordt alleen een nieuwe thread gemaakt als de wachtrij vol is.
voordelen:
BlockingQueue-grootte kan worden geregeld en scenario's met onvoldoende geheugen kunnen worden voorkomen. Applicatieprestaties worden niet verslechterd met een beperkte begrensde wachtrijgrootte.
U kunt bestaande gebruiken of een nieuw Rejection Handler-beleid maken.
In de standaard ThreadPoolExecutor.AbortPolicy gooit de handler een runtime RejectedExecutionException na afwijzing.
In
ThreadPoolExecutor.CallerRunsPolicy
wordt de taak uitgevoerd door de thread die zichzelf uitvoert. Dit biedt een eenvoudig feedbackcontrolemechanisme dat het aantal nieuwe taken vertraagt.In
ThreadPoolExecutor.DiscardPolicy
wordt een taak die niet kan worden uitgevoerd eenvoudig verwijderd.In
ThreadPoolExecutor.DiscardOldestPolicy
, als de uitvoerder niet wordt afgesloten, wordt de taak aan het hoofd van de werkwachtrij verwijderd en wordt de uitvoering opnieuw geprobeerd (wat opnieuw kan mislukken, waardoor dit wordt herhaald.)
Custom
ThreadFactory
kan worden geconfigureerd, wat handig is:- Om een meer beschrijvende threadnaam in te stellen
- De thread-daemonstatus instellen
- Draadprioriteit instellen
Hier is een voorbeeld van het gebruik van ThreadPoolExecutor
Waarde ophalen uit berekening - op te roepen
Als uw berekening een bepaalde retourwaarde oplevert die later vereist is, is een eenvoudige uitvoerbare taak niet voldoende. In dergelijke gevallen kunt u ExecutorService.submit(
Callable
<T>)
die een waarde retourneert nadat de uitvoering is voltooid.
De service retourneert een Future
die u kunt gebruiken om het resultaat van de uitvoering van de taak op te halen.
// Submit a callable for execution
ExecutorService pool = anExecutorService;
Future<Integer> future = pool.submit(new Callable<Integer>() {
@Override public Integer call() {
//do some computation
return new Random().nextInt();
}
});
// ... perform other tasks while future is executed in a different thread
Als u het resultaat van de toekomst nodig hebt, belt u future.get()
Wacht onbeperkt tot de toekomst met een resultaat eindigt.
try { // Blocks current thread until future is completed Integer result = future.get(); catch (InterruptedException || ExecutionException e) { // handle appropriately }
Wacht tot de toekomst is voltooid, maar niet langer dan de opgegeven tijd.
try { // Blocks current thread for a maximum of 500 milliseconds. // If the future finishes before that, result is returned, // otherwise TimeoutException is thrown. Integer result = future.get(500, TimeUnit.MILLISECONDS); catch (InterruptedException || ExecutionException || TimeoutException e) { // handle appropriately }
Als het resultaat van een geplande of actieve taak niet langer vereist is, kunt u Future.cancel(boolean)
bellen om deze te annuleren.
- Als
cancel(false)
beltcancel(false)
wordt de taak gewoon verwijderd uit de wachtrij die moet worden uitgevoerd. - Als
cancel(true)
aanroept, wordt de taak ook onderbroken als deze momenteel wordt uitgevoerd.
Plannen dat taken op een vast tijdstip, na een vertraging of herhaaldelijk worden uitgevoerd
De klasse ScheduledExecutorService
biedt een methode voor het plannen van enkele of herhaalde taken op een aantal manieren. In het volgende codevoorbeeld wordt ervan uitgegaan dat de pool
als volgt is gedeclareerd en geïnitialiseerd:
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
Naast de normale ExecutorService
methoden, voegt de API ScheduledExecutorService
4 methoden toe die taken plannen en objecten ScheduledFuture
retourneren. Dit laatste kan worden gebruikt om (in sommige gevallen) resultaten op te halen en taken te annuleren.
Een taak starten na een vaste vertraging
In het volgende voorbeeld wordt een taak gepland om na tien minuten te starten.
ScheduledFuture<Integer> future = pool.schedule(new Callable<>() {
@Override public Integer call() {
// do something
return 42;
}
},
10, TimeUnit.MINUTES);
Taken starten tegen een vast tarief
In het volgende voorbeeld wordt een taak gepland om na tien minuten te starten en vervolgens herhaaldelijk met een snelheid van één keer per minuut.
ScheduledFuture<?> future = pool.scheduleAtFixedRate(new Runnable() {
@Override public void run() {
// do something
}
},
10, 1, TimeUnit.MINUTES);
De uitvoering van de taak zal doorgaan volgens het schema totdat de pool
wordt afgesloten, de future
wordt geannuleerd of een van de taken op een uitzondering stuit.
Het is gegarandeerd dat de taken die zijn gepland met een bepaalde scheduledAtFixedRate
oproep elkaar niet op tijd overlappen. Als een taak langer duurt dan de voorgeschreven periode, kunnen de volgende en volgende taakuitvoering te laat beginnen.
Taken starten met een vaste vertraging
In het volgende voorbeeld wordt een taak gepland om na tien minuten te starten en vervolgens herhaaldelijk met een vertraging van één minuut tussen het einde van de ene taak en het volgende.
ScheduledFuture<?> future = pool.scheduleWithFixedDelay(new Runnable() {
@Override public void run() {
// do something
}
},
10, 1, TimeUnit.MINUTES);
De uitvoering van de taak zal doorgaan volgens het schema totdat de pool
wordt afgesloten, de future
wordt geannuleerd of een van de taken op een uitzondering stuit.
Afgewezen uitvoering verwerken
Als
- u probeert taken in te dienen bij een shutdown-uitvoerder of
- de wachtrij is verzadigd (alleen mogelijk met begrensde) en het maximale aantal threads is bereikt,
RejectedExecutionHandler.rejectedExecution(Runnable, ThreadPoolExecutor)
wordt aangeroepen.
Het standaardgedrag is dat een RejectedExecutionException naar de beller wordt gegooid. Maar er zijn meer vooraf gedefinieerde gedragingen beschikbaar:
- ThreadPoolExecutor.AbortPolicy (standaard, gooit REE)
- ThreadPoolExecutor.CallerRunsPolicy (voert taak uit op thread van beller - blokkeert deze )
- ThreadPoolExecutor.DiscardPolicy (taak in stilte weggooien)
- ThreadPoolExecutor.DiscardOldestPolicy (verwijder oudste taak in de wachtrij en probeer de nieuwe taak opnieuw uit te voeren)
Je kunt ze instellen met een van de ThreadPool- constructors :
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) // <--
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) // <--
Je kunt ook je eigen gedrag implementeren door de RejectedExecutionHandler- interface uit te breiden:
void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
submit () vs execute () uitzonderingsafhandelingsverschillen
In het algemeen wordt het commando execute () gebruikt voor het vuur en vergeet oproepen (zonder het resultaat te analyseren) en het commando submit () wordt gebruikt voor het analyseren van het resultaat van het Future-object.
We moeten ons bewust zijn van het belangrijkste verschil tussen mechanismen voor het omgaan met uitzonderingen tussen deze twee opdrachten.
Uitzonderingen op submit () worden door het kader ingeslikt als u ze niet hebt gevangen.
Codevoorbeeld om het verschil te begrijpen:
Geval 1: dien de opdracht Runnable met execute () in, die de uitzondering meldt.
import java.util.concurrent.*;
import java.util.*;
public class ExecuteSubmitDemo {
public ExecuteSubmitDemo() {
System.out.println("creating service");
ExecutorService service = Executors.newFixedThreadPool(2);
//ExtendedExecutor service = new ExtendedExecutor();
for (int i = 0; i < 2; i++){
service.execute(new Runnable(){
public void run(){
int a = 4, b = 0;
System.out.println("a and b=" + a + ":" + b);
System.out.println("a/b:" + (a / b));
System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
}
});
}
service.shutdown();
}
public static void main(String args[]){
ExecuteSubmitDemo demo = new ExecuteSubmitDemo();
}
}
class ExtendedExecutor extends ThreadPoolExecutor {
public ExtendedExecutor() {
super(1, 1, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
}
// ...
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try {
Object result = ((Future<?>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
if (t != null)
System.out.println(t);
}
}
output:
creating service
a and b=4:0
a and b=4:0
Exception in thread "pool-1-thread-1" Exception in thread "pool-1-thread-2" java.lang.ArithmeticException: / by zero
at ExecuteSubmitDemo$1.run(ExecuteSubmitDemo.java:15)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
java.lang.ArithmeticException: / by zero
at ExecuteSubmitDemo$1.run(ExecuteSubmitDemo.java:15)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Geval 2: vervang execute () door submit (): service.submit(new Runnable(){
In dit geval worden uitzonderingen door het raamwerk ingeslikt omdat de methode run () ze niet expliciet heeft opgevangen.
output:
creating service
a and b=4:0
a and b=4:0
Geval 3: Wijzig de newFixedThreadPool in ExtendedExecutor
//ExecutorService service = Executors.newFixedThreadPool(2);
ExtendedExecutor service = new ExtendedExecutor();
output:
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero
a and b=4:0
java.lang.ArithmeticException: / by zero
Ik heb dit voorbeeld gedemonstreerd om twee onderwerpen te behandelen: Gebruik uw aangepaste ThreadPoolExecutor en behandel Exectpion met aangepaste ThreadPoolExecutor.
Andere eenvoudige oplossing voor het bovenstaande probleem: wanneer u de normale ExecutorService & submit-opdracht gebruikt, haalt u het Future-object uit de submit () opdrachtoproep get () API op Future. Vang de drie uitzonderingen, die zijn geciteerd in de implementatie van de methode AfterExecute. Voordeel van aangepaste ThreadPoolExecutor ten opzichte van deze aanpak: u hoeft het afhandelingsmechanisme voor uitzonderingen slechts op één plaats af te handelen - Custom ThreadPoolExecutor.
Gebruik cases voor verschillende soorten gelijktijdigheidsconstructies
ExecutorService executor = Executors.newFixedThreadPool(50);
Het is eenvoudig en gemakkelijk te gebruiken. Het verbergt details op laag niveau van
ThreadPoolExecutor
.Ik geef de voorkeur aan deze wanneer het aantal
Callable/Runnable
taken klein in aantal is en het stapelen van taken in onbeperkte wachtrij het geheugen niet verhoogt en de prestaties van het systeem verslechtert. Als uCPU/Memory
, gebruik ik lieverThreadPoolExecutor
met capaciteitsbeperkingen &RejectedExecutionHandler
om afwijzing van taken af te handelen.CountDownLatch
wordt geïnitialiseerd met een bepaalde telling. Dit aantal wordt verlaagd door aanroepen van de methodecountDown()
. Threads die wachten tot deze telling nul bereikt, kunnen een van deawait()
methoden aanroepen. Het aanroepen vanawait()
blokkeert de thread totdat de telling nul bereikt. Met deze klasse kan een Java-thread wachten tot een andere reeks threads hun taken heeft voltooid.Gebruik gevallen:
Maximaal parallellisme bereiken: soms willen we een aantal threads tegelijkertijd starten om maximaal parallellisme te bereiken
Wacht N threads om te voltooien voordat u de uitvoering start
Deadlock-detectie.
ThreadPoolExecutor : het biedt meer controle. Als de toepassing wordt beperkt door het aantal openstaande / opvraagbare taken die in behandeling zijn, kunt u de begrensde wachtrij gebruiken door de maximale capaciteit in te stellen. Zodra de wachtrij de maximale capaciteit bereikt, kunt u RejectionHandler definiëren. Java biedt vier soorten
RejectedExecutionHandler
beleid .ThreadPoolExecutor.AbortPolicy
, de handler gooit een runtime RejectedExecutionException na afwijzing.ThreadPoolExecutor.CallerRunsPolicy`, de thread die zichzelf uitvoert voert de taak uit. Dit biedt een eenvoudig feedbackcontrolemechanisme dat het aantal nieuwe taken vertraagt.
In
ThreadPoolExecutor.DiscardPolicy
wordt een taak die niet kan worden uitgevoerd eenvoudig verwijderd.ThreadPoolExecutor.DiscardOldestPolicy
, als de uitvoerder niet wordt afgesloten, wordt de taak aan het begin van de werkwachtrij verwijderd en wordt de uitvoering opnieuw geprobeerd (wat opnieuw kan mislukken, waardoor dit wordt herhaald.)
Als u CountDownLatch
gedrag wilt simuleren, kunt u de methode invokeAll()
gebruiken.
Nog een mechanisme dat u niet citeerde is ForkJoinPool
De
ForkJoinPool
is toegevoegd aan Java in Java 7. DeForkJoinPool
is vergelijkbaar met de JavaExecutorService
maar met één verschil. DeForkJoinPool
maakt het gemakkelijk voor taken om hun werk op te splitsen in kleinere taken die dan ook aan deForkJoinPool
worden voorgelegd. Het stelen van taken vindt plaats inForkJoinPool
wanneer vrijeForkJoinPool
taken stelen uit de drukke wachtrij voor werkthreads.Java 8 heeft nog een API in ExecutorService geïntroduceerd om werkstelenpool te creëren. Je hoeft niet te maken
RecursiveTask
enRecursiveAction
maar kan nog steeds gebruik maken vanForkJoinPool
.public static ExecutorService newWorkStealingPool()
Creëert een werkstelende threadpool met alle beschikbare processors als doelparallelliteitsniveau.
Standaard duurt het aantal CPU-cores als parameter.
Al deze vier mechanismen vullen elkaar aan. Afhankelijk van het niveau van granulariteit dat u wilt beheren, moet u de juiste kiezen.
Wacht tot alle taken in ExecutorService zijn voltooid
Laten we eens kijken naar verschillende opties om te wachten voor de voltooiing van de ingediende taken Uitvoerder
- ExecutorService
invokeAll()
Voert de gegeven taken uit en retourneert een lijst met Futures met hun status en resultaten wanneer alles is voltooid.
Voorbeeld:
import java.util.concurrent.*;
import java.util.*;
public class InvokeAllDemo{
public InvokeAllDemo(){
System.out.println("creating service");
ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
List<MyCallable> futureList = new ArrayList<MyCallable>();
for (int i = 0; i < 10; i++){
MyCallable myCallable = new MyCallable((long)i);
futureList.add(myCallable);
}
System.out.println("Start");
try{
List<Future<Long>> futures = service.invokeAll(futureList);
} catch(Exception err){
err.printStackTrace();
}
System.out.println("Completed");
service.shutdown();
}
public static void main(String args[]){
InvokeAllDemo demo = new InvokeAllDemo();
}
class MyCallable implements Callable<Long>{
Long id = 0L;
public MyCallable(Long val){
this.id = val;
}
public Long call(){
// Add your business logic
return id;
}
}
}
Een synchronisatiehulpmiddel waarmee een of meer threads kunnen wachten tot een reeks bewerkingen die in andere threads worden uitgevoerd, is voltooid.
Een CountDownLatch wordt geïnitialiseerd met een bepaalde telling. De methoden wachten wachten tot de huidige telling nul bereikt vanwege aanroepen van de methode
countDown()
, waarna alle wachtende threads worden vrijgegeven en alle daaropvolgende aanroepen van wachten onmiddellijk terugkeren. Dit is een eenmalig verschijnsel - de telling kan niet worden gereset. Als u een versie nodig hebt die de telling opnieuw instelt , kunt u overwegen een CyclicBarrier te gebruiken .ForkJoinPool of
newWorkStealingPool()
in ExecutorsDoorloop alle
Future
objecten die zijn gemaakt na verzending naarExecutorService
Aanbevolen manier om de Oracle-documentatiepagina van ExecutorService af te sluiten :
void shutdownAndAwaitTermination(ExecutorService pool) { pool.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being cancelled if (!pool.awaitTermination(60, TimeUnit.SECONDS)) System.err.println("Pool did not terminate"); } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted pool.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); }
shutdown():
Start een geordende afsluiting waarin eerder ingediende taken worden uitgevoerd, maar geen nieuwe taken worden geaccepteerd.shutdownNow():
Pogingen om alle actief uitgevoerde taken te stoppen, stopt de verwerking van wachtende taken en retourneert een lijst met taken die in afwachting waren van uitvoering.Als in het bovenstaande voorbeeld meer tijd nodig is om uw taken uit te voeren, kunt u de voorwaarde wijzigen in de voorwaarde voorwaarde
Vervangen
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
met
while(!pool.awaitTermination(60, TimeUnit.SECONDS)) { Thread.sleep(60000);
}
Gebruik cases voor verschillende soorten ExecutorService
Executors retourneert een ander type ThreadPools voor specifieke behoeften.
public static ExecutorService newSingleThreadExecutor()
Maakt een uitvoerder die een enkele werkthread gebruikt die werkt vanuit een onbeperkte wachtrij
Er is een verschil tussen
newFixedThreadPool(1)
ennewSingleThreadExecutor()
zoals de java-doc voor de laatste zegt:In tegenstelling tot de overigens equivalente newFixedThreadPool (1) is de geretourneerde uitvoerder gegarandeerd niet herconfigureerbaar om extra threads te gebruiken.
Wat betekent dat een
newFixedThreadPool
later in het programma opnieuw kan worden geconfigureerd door:((ThreadPoolExecutor) fixedThreadPool).setMaximumPoolSize(10)
Dit is niet mogelijk voornewSingleThreadExecutor
Gebruik gevallen:
- U wilt de ingediende taken in een reeks uitvoeren.
- U hebt slechts één thread nodig om al uw verzoeken af te handelen
nadelen:
- Onbeperkte wachtrij is schadelijk
public static ExecutorService newFixedThreadPool(int nThreads)
Creëert een thread pool die een vast aantal threads hergebruikt die werken vanuit een gedeelde onbegrensde wachtrij. Op elk moment zullen de meeste nThreads-threads actieve verwerkingstaken zijn. Als er extra taken worden verzonden wanneer alle threads actief zijn, wachten ze in de wachtrij totdat er een thread beschikbaar is
Gebruik gevallen:
- Effectief gebruik van beschikbare kernen.
nThreads
alsRuntime.getRuntime().availableProcessors()
- Wanneer u besluit dat aantal threads niet hoger mag zijn dan een nummer in de threadpool
nadelen:
- Onbeperkte wachtrij is schadelijk.
- Effectief gebruik van beschikbare kernen.
public static ExecutorService newCachedThreadPool()
Hiermee maakt u een threadpool die indien nodig nieuwe threads maakt, maar eerder geconstrueerde threads opnieuw gebruikt wanneer deze beschikbaar zijn
Gebruik gevallen:
- Voor asynchrone taken van korte duur
nadelen:
- Onbeperkte wachtrij is schadelijk.
- Elke nieuwe taak maakt een nieuwe thread als alle bestaande threads bezet zijn. Als de taak lang duurt, worden er meer threads gemaakt, waardoor de prestaties van het systeem achteruitgaan. Alternatief in dit geval:
newFixedThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
Hiermee maakt u een threadpool die kan plannen dat opdrachten na een bepaalde vertraging worden uitgevoerd of periodiek worden uitgevoerd.
Gebruik gevallen:
- Omgaan met terugkerende gebeurtenissen met vertragingen, die in de toekomst op bepaalde tijdstippen zullen gebeuren
nadelen:
- Onbeperkte wachtrij is schadelijk.
5.
public static ExecutorService newWorkStealingPool()
Creëert een werkstelende threadpool met alle beschikbare processors als doelparallelliteitsniveau
Gebruik gevallen:
- Voor het verdelen en overwinnen van soort problemen.
- Effectief gebruik van niet-actieve threads. Niet-actieve threads steelt taken van drukke threads.
nadelen:
- Onbeperkte wachtrijgrootte is schadelijk.
U kunt een van de vele nadelen zien in al deze ExecutorService: onbeperkte wachtrij. Dit wordt aangepakt met ThreadPoolExecutor
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
Met ThreadPoolExecutor
kunt u dat
- Beheer de schroefdraadpool dynamisch
- Stel de capaciteit in voor
BlockingQueue
- Definieer
RejectionExecutionHander
wanneer de wachtrij vol is -
CustomThreadFactory
om wat extra functionaliteit toe te voegen tijdens het maken van een thread(public Thread newThread(Runnable r)
Thread Pools gebruiken
Thread Pools worden meestal gebruikt in ExecutorService
.
De volgende methoden kunnen worden gebruikt om werk in te dienen voor uitvoering:
Methode | Beschrijving |
---|---|
submit | Voert het ingediende werk uit en retourneert een toekomst die kan worden gebruikt om het resultaat te krijgen |
execute | Voer de taak ergens in de toekomst uit zonder enige retourwaarde te krijgen |
invokeAll | Voer een lijst met taken uit en retourneer een lijst met Futures |
invokeAny | Voert alles uit, maar retourneert alleen het resultaat van een resultaat dat succesvol is (zonder uitzonderingen) |
Als je klaar bent met de threadpool, kun je shutdown()
gebruiken om de threadpool te beëindigen. Hiermee worden alle taken in behandeling uitgevoerd. Om te wachten tot alle taken zijn uitgevoerd, kun je lus awaitTermination
of isShutdown()
.