Les amis qui ont utilisé des forfaits concurrencés Java connaissent peut-être Future (Interface). L'avenir est même directement pris en charge au niveau de la syntaxe dans certaines langues de domaine (comme Alice ML).
Ici, nous prendrons java.util.concurrent.future comme exemple pour parler brièvement de la méthode de travail spécifique d'avenir. L'objet futur lui-même peut être considéré comme une référence explicite, une référence aux résultats du traitement asynchrone. En raison de sa nature asynchrone, l'objet qu'il fait référence peut ne pas être disponible au début de la création (par exemple, il est toujours en fonctionnement, transmission de réseau ou attente). À l'heure actuelle, si le flux du programme qui devient futur n'est pas pressé d'utiliser l'objet référencé par l'avenir, il peut faire tout ce que vous souhaitez. être deux situations:
J'espère voir cet objet disponible et terminer certains processus de suivi connexes. S'il n'est vraiment pas disponible, vous pouvez également saisir d'autres processus de branche.
"Sans toi, ma vie perdra son sens, donc même si la mer est partie, je t'attendrai." -temps)
Pour le premier cas, vous pouvez déterminer si l'objet référencé est prêt en appelant Future.isdone () et prendre un traitement différent pour le dernier cas, vous n'avez qu'à appeler get () ou
Get (temps d'attente à longue durée, unité TimeUnit) attend que l'objet soit prêt par le blocage synchrone. Que le temps d'exécution réel soit bloqué ou renvoyé immédiatement dépend de l'heure d'appel de get () et de l'ordre de l'objet prêt.
En termes simples, le mode futur peut répondre aux besoins de concurrence basés sur les données dans les processus continus, non seulement à obtenir des améliorations de performances dans l'exécution simultanée, mais aussi à la simplicité et à l'élégance des processus continus.
Comparaison avec d'autres modèles de conception simultanés
En plus de l'avenir, d'autres modèles de conception de concurrence communs incluent «Rappel axé sur le rappel (dans un environnement multi-thread)», «Message / événement piloté (dans le modèle d'acteur)», etc.
Le rappel est le mode de concurrence asynchrone le plus courant, qui a une immédiateté élevée et une conception d'interface simple. Mais par rapport à l'avenir, ses inconvénients sont également très évidents. Premièrement, les rappels dans les environnements multithread sont généralement exécutés dans le thread du module qui déclenche le rappel, ce qui signifie que les problèmes d'exclusion mutuelle doivent être pris en compte lors de la rédaction de méthodes de rappel; Exécuter des rappels pour les applications utilisateur, qui sont également relativement dangereuses, car vous ne pouvez pas déterminer combien de temps cela prendra ou quelles exceptions cela se produira, ce qui peut indirectement affecter l'immédiateté et la fiabilité de ce module; Séquence. Par conséquent, l'interface de rappel convient aux scénarios où seules les tâches simples doivent être effectuées dans un rappel et ne doivent pas être combinées avec d'autres processus.
Les inconvénients des modes de rappel ci-dessus sont exactement les forces de l'avenir. Étant donné que l'utilisation de l'avenir est d'incorporer naturellement des conducteurs de données asynchrones dans des processus séquentiels, vous n'avez pas du tout à considérer les problèmes de fil Mutex. être mentionné ci-dessous "avenir paresseux"). D'un autre côté, les modules qui fournissent de futures interfaces n'ont pas à se soucier des problèmes de fiabilité comme les interfaces de rappel et leur impact d'immédiateté potentiel sur ce module.
Un autre modèle de conception concurrent commun est "Message (événement) conduit", qui est généralement utilisé dans le modèle d'acteur: le demandeur de service envoie un message au fournisseur de services, puis continue d'effectuer des tâches ultérieures qui ne s'appuient pas sur les résultats du traitement de service , et en dépend si vous avez besoin de s'appuyer sur lui. Bien que ce contrôle simultané basé sur la machine d'état soit plus adapté aux processus séquentiels de continuité que les rappels, les développeurs doivent réduire le processus continu en plusieurs sous-processus spécifiques à l'état selon l'appel du service asynchrone, qui augmente artificiellement la complexité du développement. L'utilisation du mode futur peut éviter ce problème et ne pas avoir à casser les processus continus pour les appels asynchrones. Cependant, une chose doit être notée: la méthode future.get () peut bloquer l'exécution du thread, donc elle ne peut généralement pas être directement incorporée dans le modèle d'acteur conventionnel. (Le modèle d'acteur basé sur la coroutine peut mieux résoudre ce conflit)
La flexibilité de l'avenir se reflète également dans son libre choix entre la synchronisation et les choix asynchrones. les besoins du processus. Par exemple, vous pouvez décider d'utiliser cet écart pour effectuer d'autres tâches en fonction de la présence des données, ce qui est tout à fait pratique pour la mise en œuvre du mécanisme de "prédiction asynchrone de la branche".
Le dérivé de l'avenir
En plus des formes de base mentionnées ci-dessus, Future a également de riches changements dérivés, donc voici quelques-uns courants.
Avenir paresseux
Contrairement aux futurs généraux, Lazy Future ne commence pas activement à préparer l'objet référencé au début de la création, mais attend que l'objet soit demandé avant de commencer les travaux correspondants. Par conséquent, un avenir paresseux lui-même n'est pas destiné à atteindre la concurrence, mais prend des ressources informatiques inutiles comme point de départ, et son effet est similaire à Lambda / Close. Par exemple, lors de la conception de certaines API, vous devrez peut-être retourner un ensemble d'informations, et le calcul de certains d'entre eux peut consommer des ressources considérables. Cependant, l'appelant peut ne pas être préoccupé par toutes ces informations, donc la fourniture d'objets qui nécessitent plus de ressources sous la forme d'un avenir paresseux peuvent économiser des ressources lorsque l'appelant n'a pas besoin d'utiliser des informations spécifiques.
De plus, un avenir paresseux peut également être utilisé pour éviter les exclusions mutuelles inutiles causées par l'acquisition prématurée ou le verrouillage des ressources.
Promesse
La promesse peut être considérée comme une branche spéciale de l'avenir. Mais la promesse est utilisée pour représenter explicitement des scénarios où les processus asynchrones ne sont pas directement déclenchés par l'appelant de service. Par exemple, le contrôle de synchronisation de l'interface future, son processus asynchrone n'est pas déclenché par l'appelant, mais par l'exemple. abonné, mais par l'abonné. Par conséquent, par rapport à l'avenir standard, l'interface de promesse a généralement un ensemble supplémentaire () ou une interface épanoui ().
Avenir réutilisable
Un avenir régulier est unique, ce qui signifie que lorsque vous obtenez des résultats de traitement asynchrones, l'objet futur lui-même perd son sens. Mais un avenir spécialement conçu peut également être réutilisé, ce qui est très utile pour les données qui peuvent être modifiées plusieurs fois. Par exemple, l'interface de style futur fournie par le cadre d'abonnement distribué Taobao mentionné ci-dessus permet à plusieurs appels de WaitNext () (équivalent à futur.get ()). Dernier appel. L'avantage de cette conception est que l'utilisateur de l'interface peut répondre aux modifications des données d'abonnement à tout moment appropriées, ou simplement via une boucle infinie dans un thread indépendant, tout en prenant en compte d'autres tâches de synchronisation, ou même en attendant plusieurs tâches en même temps. Les exemples simplifiés sont les suivants:
pour (;;) {schedule = getNexTScheduledTaskTime (); .}} DoScheduledTask ();} Utilisation de l'avenir
Tout d'abord, listez un morceau de code en Java.
//: concurrencée / callablemable.javaimport java.util.concurrent. String Call () {return "Résultat de TaskWithResult" + ID;}} CALDAGE PUBLIQUE CALLALEDEMO {public static void main (String [] args) {EMMIRYSERVICE EX EC = EXEMBORTORS.NEWCACHEDTHERDPOOL (); Nouveau arrayList <futur <string >> (); essayer : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :::::::::::::::::::::::::::::::::::- : :::::::::::::::::::::::::::::::: {// Get () bloque jusqu'à la fin: System.out.println (fs.get ()) ; : Résultat de la tâche avec un résultant de la tâche avec un résultant de la tâche avec un respect de la tâche de la tâche avec une tâche de tâche avec un résulte de tâche avec un résulte de tâche avec une tâche de tâche 9 * //: ~Pour expliquer l'utilisation de l'avenir, d'abord, l'objet ExecutorService Exec appelle la méthode Soumide () pour générer un futur objet, qui utilise le type spécifique du résultat de retour callable à Paramétrize. Vous pouvez utiliser la méthode isDone () pour demander si l'avenir a été achevé. Lorsque la tâche est terminée, elle a un résultat et vous pouvez appeler la méthode get () pour obtenir le résultat. Vous pouvez également appeler get () directement sans vérification iSDOne (). Vous pouvez appeler la fonction get () avec un délai d'expiration, ou appeler IsDone () d'abord pour voir si la tâche est terminée, puis appelez get ().