Java Concurrence: Cadre d’exĂ©cution

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 classeCallableet est paramĂ©trĂ© pourStringType. 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 et vérifiez votre sortie:

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.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *