Introduction
Avec l’augmentation du nombre de cĆurs disponibles dans les processeurs aujourd’hui, le long de la journĂ©e. Avec la nĂ©cessitĂ© croissante d’atteindre une plus grande performance, les API multi-threads deviennent assez populaires. Java proporciona su propio marco de subprocesos mĂșltiples llamado Marco del ejecutor.
¿Qué es Executor Framework?
Executor Framework contiene una serie de componentes que se utilizan para administrar de manera eficiente los subprocesos de travail. L’API de l’exĂ©cuteur dĂ©coule l’exĂ©cution de la tĂąche de tĂąche rĂ©elle qui sera exĂ©cutĂ©e par Executors
. Cette conception est l’une des implĂ©mentations du modĂšle de producteur-consommateur.
Le java.util.concurrent.Executors
Fournissez des mĂ©thodes d’usine utilisĂ©s pour crĂ©er ThreadPools
de threads de travail.
Utiliser le cadre exĂ©cutant, nous devons crĂ©er l’un de ces groupes de sous-processus et envoyer la tĂąche Ă exĂ©cuter. Est le travail du programme-cadre exĂ©cuteur et exĂ©cuter les tĂąches envoyĂ©es et renvoyer les rĂ©sultats du groupe de sous-traitĂ©s.
Une question de base qui me vient Ă l’esprit est la raison de ce que nous avons besoin de tels groupes de sous-processus lorsque nous pouvons crĂ©er des objets de java.lang.Thread
ou implémenter Runnable
/
interfaces pour atteindre le parallélisme?
La réponse est réduite à deux faits de base:
- La crĂ©ation d’un nouveau thread pour une nouvelle tĂąche implique une surcharge de crĂ©ation et de dĂ©montage du fil. La gestion du cycle de vie de ce fil augmente considĂ©rablement l’heure d’exĂ©cution.
- ajoutez un nouveau fil pour chaque processus sans aucun type de limitation conduit Ă la crĂ©ation d’un grand nombre de fils. Ces sous-processus occupent la mĂ©moire et provoquent des ressources de dĂ©chets. La CPU commence Ă consacrer trop de temps Ă changer de contexte lorsque chaque thread est Ă©changĂ© et qu’un autre thread est entrĂ© pour l’exĂ©cution.
Tous ces facteurs réduisent les performances du systÚme. Les groupes de sous-processus surmontez ce problÚme en gardant les threads vivants et en les réutilisant. Toutes les tùches excédentaires qui passent de quelles sous-processus dans le groupe peuvent gérer sont conservées dans un Queue
. Une fois que l’un des threads est libĂ©rĂ©, ils occupent la tĂąche suivante de cette file d’attente. Cette file d’attente de tĂąches est essentiellement illimitĂ©e pour les exĂ©cuteurs prĂȘts Ă ĂȘtre utilisĂ©s fournis par la JDK.
Types de exécuteurs
Maintenant que nous avons une bonne idĂ©e de ce que c’est un exĂ©cuteur exĂ©cutif, Prenez Ă©galement un point sur les diffĂ©rents types d’exĂ©cutants.
Singlethreadexececor
Cet exĂ©cuteur de groupe de sous-traducs n’a qu’un fil. Il est utilisĂ© pour exĂ©cuter des tĂąches sĂ©quentiellement. Si le thread dĂ©cĂšde en raison d’une exception lors de l’exĂ©cution d’une tĂąche, un nouveau thread est crĂ©Ă© pour remplacer l’ancien fil et les tĂąches suivantes sont exĂ©cutĂ©es dans le nouveau.
ExecutorService executorService = Executors.newSingleThreadExecutor()
fixeTheadpool (n)
Comme son nom l’indique, il s’agit d’un groupe de sous-processus d’un nombre fixe de sous-processus. Les tĂąches envoyĂ©es Ă l’exĂ©cuteur exĂ©cutant sont exĂ©cutĂ©es par les threads n
et s’il y a plus de tĂąches sont stockĂ©es dans un LinkedBlockingQueue
. Ce nombre est généralement le nombre total de sous-processus supportés par le processeur sous-jacent.
ExecutorService executorService = Executors.newFixedThreadPool(4);
CachedTheadPool
Ce groupe de sous-processus est utilisĂ© principalement lorsque Il existe de nombreuses tĂąches parallĂšles de courte durĂ©e Ă exĂ©cuter. Contrairement au groupe de fils fixes, le nombre de sous-processus de ce groupe d’exĂ©cuteurs n’est pas limitĂ©. Si tous les threads sont occupĂ©s Ă exĂ©cuter certaines tĂąches et une nouvelle marque, le groupe crĂ©era et ajoute une nouvelle sous-processus Ă l’exĂ©cuteur exĂ©cutante. DĂšs que l’une des sous-curses est gratuite, elle reprendra l’exĂ©cution des nouvelles tĂąches. Si un fil reste inactif pendant soixante secondes, ils sont finis et retirĂ©s du cache.
Cependant, s’il n’est pas administrĂ© correctement ou que les tĂąches ne sont pas de courte durĂ©e, le groupe de sous-processus aura beaucoup actif. sous-processus. Cela peut conduire Ă une Ă©limination des ressources et, par consĂ©quent, Ă une chute de performance.
ExecutorService executorService = Executors.newCachedThreadPool();
fiscal programmé
Cet exécuteur utilisé il est utilisé quand nous Avoir une tùche à exécuter à intervalles réguliers ou si nous voulons retarder une certaine tùche.
ScheduledExecutorService scheduledExecService = Executors.newScheduledThreadPool(1);
Les tĂąches peuvent ĂȘtre programmĂ©es dans ScheduledExecutor
en utilisant l’une des deux mĂ©thodes scheduleAtFixedRate
ou scheduleWithFixedDelay
scheduledExecService.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
scheduledExecService.scheduleWithFixedDelay(Runnable command, long initialDelay, long period, TimeUnit unit)
La principale différence entre les deux méthodes est son interprétation du délai entre les exécutions consécutives de travaux programmés.
scheduleAtFixedRate
exécute la tùche avec un intervalle fixe, quelle que soit la tùche précédente terminée.
scheduleWithFixedDelay
démarrera le compte à rebours uniquement aprÚs la fin de la tùche actuelle.
Comprendre l’objet futur
Vous pouvez accĂ©der au rĂ©sultat de la tĂąche envoyĂ©e Ă une exĂ©cution Ă un Executor Ă l’aide de java.util.concurrent.Future
Objet renvoyĂ© par l’exĂ©cuteur exĂ©cutif. L’avenir peut ĂȘtre considĂ©rĂ© comme une promesse faite par l’exĂ©cuteur exĂ©cutante Ă l’appelant.
Future<String> result = executorService.submit(callableTask);
Une tĂąche envoyĂ©e Ă l’exĂ©cuteur, comme le prĂ©cĂ©dent, est asynchrone, est asynchrone, est asynchrone, Autrement dit, l’exĂ©cution du programme n’attend pas que l’exĂ©cution de la tĂąche passe Ă l’Ă©tape suivante. Au lieu de cela, chaque fois que l’exĂ©cution de la tĂąche est terminĂ©e, elle est Ă©tablie dans cette Future
objet par l’albacĂ©e.
L’appelant peut continuer Ă exĂ©cuter le programme principal et Lorsque le rĂ©sultat de la tĂąche envoyĂ©e est nĂ©cessaire, vous pouvez appeler .get()
dans cet Future
objet. Si la tĂąche est terminĂ©e, le rĂ©sultat est immĂ©diatement renvoyĂ© Ă l’appelant ou, sinon, l’appelant est bloquĂ© jusqu’Ă ce que l’exĂ©cuteur exĂ©cutant achĂšve l’exĂ©cution et le rĂ©sultat est calculĂ©.
si la personne qui appelle peut ne pas se permettre indĂ©finiment avant RĂ©cupĂ©ration du rĂ©sultat, cette attente peut Ă©galement ĂȘtre chronomĂ©trĂ©e. Ceci est rĂ©alisĂ© par la Future.get(long timeout, TimeUnit unit)
méthode qui donne un TimeoutException
si le rĂ©sultat n’est pas retournĂ© dans la pĂ©riode stipulĂ©e. L’appelant peut gĂ©rer cette exception et continuer avec l’exĂ©cution ultĂ©rieure du programme.
S’il y a une exception lors de l’exĂ©cution de la tĂąche, la mĂ©thode GET donnera un ExecutionException
.
Quelque chose d’important sur le rĂ©sultat renvoyĂ© par La mĂ©thode est que celle-ci n’est renvoyĂ©e que si la tĂąche envoyĂ©e implĂ©mente java.util.concurrent.Callable
Si la tĂąche implĂ©mente l’interface Runnable
, l’appel Ă .get()
retournera
une fois la La tùche est terminée.
Une autre méthode importante est la Future.cancel(boolean mayInterruptIfRunning)
mĂ©thode. Cette mĂ©thode est utilisĂ©e pour annuler l’exĂ©cution d’une tĂąche envoyĂ©e. Si la tĂąche est dĂ©jĂ en cours d’exĂ©cution, l’exĂ©cuteur tentera d’interrompre l’exĂ©cution de la tĂąche si l’indicateur mayInterruptIfRunning
Le drapeau est transmis sous .
Exemple: Créez et exécutez une simple exécutante
Nous allons crĂ©er une tĂąche et essayer de l’exĂ©cuter dans un exĂ©cuteur de groupe fixe:
public class Task implements Callable<String> { private String message; public Task(String message) { this.message = message; } @Override public String call() throws Exception { return "Hello " + message + "!"; }}
l’ID
Implexes de la classeCallable
et est paramétré pourString
Type. Il est également déclaré en lançantException
. Cette capacitĂ© Ă lancer une exception Ă l’exĂ©cuteur exĂ©cutante et l’exĂ©cuteur qui renvoie cette exception Ă l’appelant revĂȘt une grande importance car elle aide la personne qui appelle Ă connaĂźtre le statut d’exĂ©cution de la tĂąche.
Nous exécutons maintenant cette tùche:
public class ExecutorExample { public static void main(String args) { Task task = new Task("World"); ExecutorService executorService = Executors.newFixedThreadPool(4); Future<String> result = executorService.submit(task); try { System.out.println(result.get()); } catch (InterruptedException | ExecutionException e) { System.out.println("Error occured while executing the submitted task"); e.printStackTrace(); } executorService.shutdown(); }}
Ici, nous avons créé un FixedThreadPool
ExĂ©cuteur avec un nombre de 4 threads, car cette dĂ©monstration est dĂ©veloppĂ©e dans un processeur quatre cĆurs. Le nombre de sous-processus peut ĂȘtre supĂ©rieur aux cĆurs du processeur si les tĂąches exĂ©cutĂ©es effectuent des opĂ©rations d’E / S considĂ©rables ou de passer du temps Ă attendre des ressources externes.
Nous avons instancié le Task
classe et prenez-le Ă l’exĂ©cuteur exĂ©cutif pour l’exĂ©cution. Le rĂ©sultat est renvoyĂ© par l’objet Future
, qui imprime ensuite sur l’Ă©cran.
Laissez-nous exécuter
Hello World!
Comme prévu, la tùche ajoute le message « Bonjour » et renvoie le résultat à travers le Future
objet.
Enfin, nous appelons au moment de l’objet executorService
objet pour terminer toutes les discussions et renvoyer les ressources au systĂšme d’exploitation.
Le .shutdown()
La mĂ©thode est actuellement l’achĂšvement des tĂąches actuellement envoyĂ©es Ă l’exĂ©cuteur exĂ©cutante. Toutefois, si l’exigence ferme immĂ©diatement l’exĂ©cuteur sans attendre, nous pouvons utiliser .shutdownNow()
MĂ©thode en place.
Toute tĂąche d’exĂ©cution en attente sera renvoyĂ©e dans un java.util.List
objet.
En outre, nous pouvons crĂ©er cette mĂȘme tĂąche en implĂ©mentant l’interface Runnable
public class Task implements Runnable{ private String message; public Task(String message) { this.message = message; } public void run() { System.out.println("Hello " + message + "!"); }}
Il y a une paire Des changements importants ici lorsque nous mettons en Ćuvre runnable.
- Le rĂ©sultat de l’exĂ©cution de la tĂąche ne peut pas ĂȘtre renvoyĂ© Ă partir de la
run()
mĂ©thode. Par consĂ©quent, nous imprimons directement d’ici. - L’ID
La mĂ©thode n’est pas configurĂ©e pour lancer une exception marquĂ©e.
Conclusion
Le sous-processus multiple devient de plus en plus courant car la vitesse de l’horloge du processeur est difficile Ă augmenter. Cependant, la manipulation du cycle de vie de chaque thread est trĂšs difficile en raison de la complexitĂ© impliquĂ©e.
Dans cet article, nous avons dĂ©montrĂ© un cadre multiple multiple mais simple, un cadre exĂ©cuteur exĂ©cutant et expliquĂ© ses diffĂ©rents composants. Nous examinons Ă©galement diffĂ©rents exemples de crĂ©ation et d’exĂ©cution de tĂąches dans un exĂ©cuteur exĂ©cutante.
Comme toujours, le code de cet exemple peut ĂȘtre trouvĂ© dans GitHub.