Il y a souvent des tâches dans le projet qui doivent être exécutées de manière asynchrone (soumise au pool de threads) pour être exécutée, tandis que le fil principal doit souvent connaître les résultats de l'exécution asynchrone. Que devons-nous faire pour le moment? Il est impossible de réaliser avec Runnable, nous devons utiliser Calable pour lire le code suivant:
Importer java.util.concurrent.Callable; import java.util.concurrent.executionException; import java.util.concurrent.executerservice; import java.util.concurrent.executors; importer java.util.concurrent.future; la classe publique addTask implémente Callable <intiger> {private int a, b; public addTask (int a, int b) {this.a = a; this.b = b; } @Override Public Integer Call lance une exception {entier résultat = a + b; Résultat de retour; } public static void main (String [] args) lève InterruptedException, ExecutionException {exécutor executorService exécutor = exécutor.newSingLethreAdExECUTOR; // JDK est revenu jusqu'à présent et sont des instances de FutureTask Future <Integer> futur = exécutor.Submit (new AddTask (1, 2)); Integer result = future.get; // uniquement lorsque l'état de l'avenir est terminé (futur.isdone = true), la méthode GET reviendra}} Bien que nous puissions réaliser l'exigence d'obtenir des résultats d'exécution asynchrones, nous avons constaté que cet avenir n'est en fait pas utile car il ne fournit pas de mécanisme de notification, ce qui signifie que nous ne savons pas quand l'avenir sera terminé (si nous devons interroger Isdone () pour juger, il semble qu'il n'y ait pas besoin de l'utiliser). Jetez un œil à la méthode d'interface de java.util.concurrent.future.future:
Interface publique Future <v> {booléen annuler (booléen MayInterrupfrunning); booléen iscancellé; Boolean Isdone; V Get Throws InterruptedException, ExecutionException; V Get (temps de temps long, unité TimeUnit) lance InterruptedException, ExecutionException, TimeoutException;} De cela, nous pouvons voir que le futur mécanisme de JDK n'est en fait pas facile à utiliser. Si vous pouvez ajouter un auditeur à cet avenir et le laisser en informer l'auditeur une fois terminé, il sera plus facile à utiliser, tout comme le suivant IFUture:
Package Future; import java.util.concurrent.cancellationException; import java.util.concurrent.future; import java.util.concurrent.timeunit; / ** * le résultat d'une opération asynchrone. * * @author lixiaoHUi * @param <v> Paramètre de type du résultat d'exécution * / interface publique Ifuture <v> étend Future <v> {Boolean Issucess; // si V getnow réussit; // Renvoie le résultat immédiatement (que l'avenir soit dans l'état terminé) une cause jetable; // La raison de l'échec de l'exécution est annulée; // Puis-je annuler ifuture <v> attendre les lancers InterruptedException; // en attente de l'achèvement futur Booléen attend (longue date de temps de temps) lance InterruptedException; // attendre le temps d'attente pour l'achèvement futur booléen attend (long délai, tempsunit timeunit) lance InterruptedException; Ifuture <v> attendre l'interruption; // En attente de l'achèvement futur, aucune interruption booléenne ne s'attend à un temps de temps de temps de temps long); // attend le délai d'attente pour l'achèvement futur, aucune réponse d'interruption booléenne ne vous attend (temps de temps, tempsUnit TimeUnit); IfUture <v> addListener (ifutureListener <v> l); // Une fois l'avenir terminé, ces auditeurs ajoutés seront notifiés ifuture <v> reveListener (ifututreListener <v> l); } Ensuite, mettons en œuvre cet ifuture ensemble. Avant cela, nous expliquerons la méthode Object.Att, object.notifyall, car le noyau de la "��" originale de toute la future implémentation est ces deux méthodes. Jetez un œil à l'explication dans JDK:
La classe publique Object {/ ** * fait attendre le thread actuel qu'un autre thread invoque la méthode * {@link java.lang.object # notify} ou la méthode * {@link java.lang.object # notifyall} pour cet objet. * En d'autres termes, cette méthode se comporte exactement comme s'il effectue simplement l'appel {@code wait (0)}. * Après avoir appelé cette méthode, le thread actuel libèrera le verrouillage du moniteur d'objet et abandonnera les droits d'utilisation du CPU. Jusqu'à ce qu'un autre thread appelle notify / notifyall * / public final void wait lance InterruptedException {wait (0); } / ** * réveille tous les threads qui attendent sur le moniteur de cet objet. Un * thread attend sur le moniteur d'un objet en appelant l'une des méthodes * {@code wait}. * <p> * Les threads éveillés ne pourront pas procéder tant que le thread actuel * n'abandonne la serrure de cet objet. Les threads éveillés * rivaliseront de la manière habituelle avec tout autre thread qui pourrait * être activement concurrentiel pour se synchroniser sur cet objet; Par exemple, * les fils éveillés ne bénéficient aucun privilège ou désavantage fiable de * étant le prochain thread à verrouiller cet objet. * / public final native void notifyall;} Après avoir su cela, nous avons l'idée de mettre en œuvre un avenir par nous-mêmes. Lorsque le thread appelle une série de méthodes telles que ifUture.Await, si l'avenir n'a pas été terminé, appelez la méthode Future.AiT pour que le thread entre dans l'état d'attente. Lorsque d'autres threads définissent l'avenir à l'état d'achèvement (notez que l'état d'achèvement comprend ici une extrémité normale et une extrémité anormale), la méthode future.NotifyAll doit être appelée pour réveiller les fils qui étaient dans l'état d'attente en raison de l'appel de la méthode d'attente. L'implémentation complète est la suivante (le code ne doit pas être difficile à comprendre. Je me réfère au futur mécanisme de netty. Si vous êtes intéressé, vous pouvez consulter le code source de Netty):
package future;import java.util.Collection;import java.util.concurrent.CancellationException;import java.util.concurrent.CopyOnWriteArrayList;import java.util.concurrent.ExecutionException;import java.util.concurrent.TimeUnit;import java.util.concurrent.TimeoutException;/** * <pre> * À l'extrémité normale, si le résultat de l'exécution n'est pas nul, le résultat est le résultat d'exécution; Si le résultat de l'exécution est nul, alors résultat = {@link abstractFuture # Success_Signal} * Lorsque l'exception se termine, le résultat est une instance de {@link CAbsalHolder}; Si l'exception se termine en raison de l'annulation, le résultat est une instance de {@link cancellationException}, sinon il s'agit d'une instance d'autres exceptions * Les situations suivantes entraîneront le transfert de l'opération asynchrone lorsque la méthode suivante est appelée à l'état suivant: * <ul> * <li> <li> Lorsque l'opération asynchrone se termine normalement (méthode du setSuccess) </li> * <li> lorsque l'opération asynchrone se termine anormalement (méthode Setfailure) </li> * </ul> * </pre> * * @author lixiaoHUi * * @param <v> * Type de l'effet asynéaire résultat * / Classe publique AbstractFuture Résultat de l'objet volatil protégé; // il doit être garanti pour être la visibilité / ** * l'ensemble d'écouteur * / collection protégée <ifutureListener <v>> écouteurs = new CopyOnwriteArrayList <ifututreListener <v>>; / ** * Lorsque le résultat d'exécution normal de la tâche est nul, c'est-à-dire lorsque le client appelle {@Link abstractFuture # setSuccess (null)}, * Résultat fait référence à l'objet * / privé static final SuccessSignal Success_Signal = new SuccessSignal; @Override public boolean annule (boolean peut-ilterrupfrunning) {if (isdone) {// return false ne peut pas être annulé; } synchronisé (this) {if (isDone) {// double vérification return false; } result = new Caoseholder (new CANCELLATIONException); notifyall; // isDone = true, aviser le thread en attente de l'attente sur l'objet} notifyListeners; // informer l'auditeur que l'opération asynchrone a été terminée Retour True; } @Override public boolean iscancellable {return result == null; } @Override public booléen iscancelled {return result! = Null && result instanceof CAbsolder && ((Causholder) Result) .Cause instanceof CANCELLATIONException; } @Override public boolean isdone {return result! = Null; } @Override public V Get Throws InterruptedException, ExecutionException {Await; // attendez le résultat de l'exécution Cause = Cause; if (cause == null) {// Aucune exception ne s'est produite, l'opération asynchrone s'est terminée normalement return GetNow; } if (cause instanceof CANCELLATIONException) {// L'opération asynchrone a été annulée Throw (CANCELLATIONException) Cause; } lancez une nouvelle ExecutionException (Cause); // Autres exceptions} @Override public V Get (temps de temps, unité de tempsUnit) lève InterruptedException, ExecutionException, TimeEutException {if (Await (Timeout, Unit)) {// Timeout en attente du résultat d'exécution Trowable Cause = Cause; if (cause == null) {// Aucune exception ne s'est produite, l'opération asynchrone s'est terminée normalement return GetNow; } if (cause instanceof CANCELLATIONException) {// L'opération asynchrone a été annulée à lancer (CANCELLATIONException); } lancez une nouvelle ExecutionException (Cause); // Autres exceptions} // Le temps n'a pas encore pris fin, lançant une exception de temps mort lance un nouveau TimeoutException; } @Override public booléen Issucess {return result == null? false :! (Résultat instance de la résumé); } @SuppressWarnings ("Unchecked") @Override public v getNow {return (v) (result == Success_Signal? Null: result); } @Override Public Throwable Cause {if (result! = Null && Result instanceof Causholder) {return ((CAbsolHolder) result) .cause; } return null; } @Override public ifuture <v> addListener (ifututreListener <v> écouteur) {if (écouteur == null) {throw new NullPointerException ("écouteur"); } if (isDone) {// Si vous avez terminé NotifyListener (écouteur); retourner ceci; } synchronisé (this) {if (! Isdone) {auditers.add (écouteur); retourner ceci; }} notifyListener (écouteur); retourner ceci; } @Override public ifuture <v> removeListERner (ifututreListener <v> écouteur) {if (écouteur == null) {throw new NullPointerException ("écouteur"); } if (! Isdone) {auditers.reMove (auditeur); } retourne ceci; } @Override public ifuture <v> Await lance InterruptedException {return wait0 (true); } private ifuture <v> attend0 (booléen interrupable) lance InterruptedException {if (! Isdone) {// Si elle a été terminée, elle sera renvoyée directement // si le terminal est autorisé et interrompu, une exception d'interruption sera lancée if (interrupable && thread.Interrupted) {lancer une nouvelle interruption (Thread "+ thread. interrompu. "); } booléen interrompu = false; synchronisé (this) {while (! Isdone) {try {wait; // Libérez le verrou et entrez l'état d'attente, attendez que d'autres threads appellent la méthode Notify / Notifyall de l'objet} Catch (InterruptedException e) {if (interrupable) {throw e; } else {interrupted = true; }}}}} if (interrompu) {// Pourquoi devons-nous définir l'indicateur d'interruption ici? Parce qu'après être retourné de la méthode d'attente, l'indicateur d'interruption est effacé, // réinitialise ici afin que d'autres codes sachent qu'il est interrompu ici. Thread.currentThread.interrupt; }} renvoie ceci; } @Override public boolean attend (long timeoutmillis) lance InterruptedException {return wait0 (timeunit.milliseconds.tonanos (timeoutmillis), true); } @Override public boolean attend (long timeout, timeUnit Unit) lève InterruptedException {return wait0 (unit.Tonanos (timeout), true); } private boolean Await0 (long timeoutnanos, booléen interrupable) lance InterruptedException {if (isDone) {return true; } if (timeoutnanos <= 0) {return isDone; } if (interrupable && thread.interrupted) {throw new interruptedException (toString); } long startTime = timeoutnanos <= 0? 0: System.NanoTime; Temps de serveur long = timeoutnanos; booléen interrompu = false; essayez {synchronisé (this) {if (isDone) {return true; } if (waittime <= 0) {return isDone; } pour (;;) {try {wait (Time Wait / 1000000, (int) (Waitime% 1000000)); } catch (InterruptedException e) {if (interrupable) {throw e; } else {interrupted = true; }} if (isDone) {return true; } else {waittime = timeoutnanos - (System.NanoTime - starttime); if (temps de serve <= 0) {return isDone; }}}}}} Enfin {if (interrompu) {thread.currentThread.interrupt; }}} @Override public ifuture <v> AwaitUnterruply {try {return wait0 (false); } catch (InterruptedException e) {// Si une exception est lancée ici, il ne peut pas être géré lancer de nouveaux java.lang.internalerror; }} @Override public Boolean AwaitUnterruply (Long TimeoutMillis) {try {return wait0 (timeunit.milliseconds.tonanos (timeoutmillis), false); } Catch (InterruptedException e) {Throw New Java.lang.internalError; }} @Override public Boolean AwaitUnterruply (long timeout, timeunit Unit) {try {return wait0 (unit.Tonanos (timeout), false); } Catch (InterruptedException e) {Throw New Java.lang.internalError; }} IFUTURE IFUTURE <V> SetFailure (Cause thrimpable) {if (setFailure0 (cause)) {NotifyListeners; retourner ceci; } Jetez un nouveau IllégalStateException ("Complete déjà:" + ceci); } private booléen setFailure0 (cause thrognable) {if (isdone) {return false; } synchronisé (this) {if (isDone) {return false; } Résultat = Nouveau Colomb (cause); notifyall; } return true; } IFUTURE IFUTURE <v> SetSuccess (résultat objet) {if (setSucCess0 (result)) {// notifyListeners après la définition avec succès; retourner ceci; } Jetez un nouveau IllégalStateException ("Complete déjà:" + ceci); } private booléen setSuccess0 (objet résultat) {if (isDone) {return false; } synchronisé (this) {if (isDone) {return false; } if (result == null) {// Le résultat de l'exécution normale de l'opération asynchrone est null this.result = Success_Signal; } else {this.result = result; } notifyall; } return true; } private void notifyListeners {for (ifuTUreListener <v> l: écouteurs) {notifyListener (l); }} private void notifyListener (ifUtutreListener <v> l) {try {l.OperationCompleted (this); } catch (exception e) {e.printStackTrace; }} classe statique privée SuccessSignal {} Classe finale statique privée Class Colomber {Final Throwable Cause; Cause en biais (cause jetable) {this.cause = cause; }}} Alors comment utiliser ceci? Avec la mise en œuvre du squelette ci-dessus, nous pouvons personnaliser divers résultats asynchrones. Ce qui suit est une tâche retardée:
Package Future.Test; Import Future.Ifuture; import future.IfutureListener; / ** * Delay Addition * @author lixiaoHUi * * / public class DelayAdder {public static void Main (String [] args) {new DelayAdder.Add (3 * 1000, 1, 2) .AddSener (New IfutureListener <Integer> OperationCompleted (ifUture <Integer> Future) lève une exception {System.out.println (futur.getNow);}}); } / ** * Delay Addition * @param Durée de retard Durée des millisecondes * @param a addition * @param b addition * @return Asynchrone Result * / public DelayAdditionFuture add (Long Delay, int a, int b) {delayAdditionFuture future = new DelayAdditionFuture; Nouveau thread (nouveau DelayAdditionTask (Delay, A, B, Future)). Démarrer; retour à l'avenir; } Classe privée DelayAdditionTask implémente Runnable {private longlay; Int privé, b; avenir privé de retard de retard; Public DelayAdditionTask (Long Delay, Int A, Int B, DelayAdditionFuture Future) {Super; this.delay = retard; this.a = a; this.b = b; this.future = futur; } @Override public void run {try {thread.sleep (delay); Entier i = a + b; // TODO Voici le futur défini sur l'état d'achèvement (l'exécution normale est terminée) Future.SetSuccess (I); } catch (InterruptedException e) {// todo voici le futur défini sur l'état d'achèvement (exception est terminé) futur.setFailure (e.getCause); }}}} package future.test; import future.abstractfuture; import future.ifuture; // il suffit d'exposer deux méthodes à la classe publique extérieure de retardAdditionfuture étend AbstractFuture <inter> {@Override public ifuture <inger> setSuccess (objet résultat) {return super.setsuccess (résultat); } @Override public ifUture <Integer> setFailure (Cause thrimpable) {return super.setFailure (cause); }} Vous pouvez voir que le client n'a pas besoin de demander activement si l'avenir est terminé, mais rappellera automatiquement la méthode OperationCompled lorsque l'avenir sera terminé. Le client doit uniquement implémenter la logique dans le rappel.
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.