2017-06-12 5 views
2

Je cours en dessous du programme, où le thread principal finit 1er, avant d'appeler la méthode get du thread enfant appelant.Comment Java conserve le résultat de la tâche future, si le thread enfant a terminé avant d'appeler la méthode get

public class Main { 
    public static void main(String[] args) throws InterruptedException, ExecutionException {   
     ExecutorService exec = Executors.newFixedThreadPool(1); 
     Future<String> f = exec.submit(new TThread()); 
     Thread.sleep(10000); 
     System.out.println("Main Thread "+System.currentTimeMillis());  
     String s = (String) f.get(); 
     System.out.println(s); 
     exec.shutdown(); 
    } 
} 

class TThread implements Callable<String>{ 
    @Override 
    public String call() throws Exception{ 
     Thread.sleep(3000); 
     System.out.println("TThread Thread "+System.currentTimeMillis()); 
     return "something"; 
    } 
} 

La sortie est

TThread Thread 1497248667516 
Main Thread 1497248674515 
something 

Mon attente était get() peut retourner null, car fil TThread a été terminé beaucoup plus tôt que pour obtenir() appel. Cela signifie-t-il que Java maintient le résultat de l'appel quel que soit l'appelant appelant get() ou non. Aussi quelqu'un peut-il expliquer, comment Java met en œuvre une telle tâche future.

+0

Pourquoi vous attendez-vous null? Dans ce cas, l'appel Tthread sera fait dans 3 secondes et le thread principal attend 10. Vous vous attendez sûrement à avoir la bonne réponse pour les 7 dernières secondes? Je pourrais voir à tort qu'il s'attendait à ce que l'inverse se produise si vous ne réalisez pas les blocs .get(), mais cette configuration retournera sûrement la réponse. – Tatarize

+0

@Tatarize, En fait, le travail du thread enfant est terminé en 3 secondes (sauf surcharge supplémentaire), pendant ce temps parent thread serait soit avant de dormir pendant 10 secondes ou dans le sommeil de 10 secondes, dans les deux cas "(String) f.get() "n'a pas encore été atteint. Donc, JIT, ne serait pas au courant de ce développeur appelé le get() ou non dans les codes à venir. Donc, mon point est; à chaque fois que submit (callable) est appelé, la JVM conserve-t-elle le résultat indépendamment du fait que get() soit appelé ou non? – Ashish

+0

Vous avez appelé get() pas appel(). Cela n'a rien à voir avec la JVM, c'est comme ça que la classe fonctionne. Tu pourrais écrire une classe similaire si tu le voulais. C'est dans le ExecutorService. – Tatarize

Répondre

0

Mon attente était get() peut retourner null ...

non, obtenir retournera seulement les valeurs possibles renvoyées par la méthode call dans la classe TThread, dans votre cas "Quelque chose", l'option à gauche est que la méthode d'appel est interrompue, puis il n'y a pas de retour du tout puisque l'exception se propage dans la pile jusqu'à l'invocation
executor.submit(). .

Votre micro-référence en Java ne fonctionne pas comme il se doit ...

1

« Alors JIT, ne serait pas au courant de ce développeur appelé get() ou non dans les codes à venir est donc mon point; à chaque fois que submit (callable) est appelé, JVM conserve-t-il le résultat indépendamment du fait que get() soit appelé ou non? "

Il me semble que votre compréhension ici est un gros problème. Tout d'abord, ni le JIT ni la JVM ne se soucient de telles commandes de haut niveau. Ils sont le résultat de la classe ExecutorService qui l'implémente. Lorsque vous l'appelez avec un résultat particulier fournissant un thread, il renvoie un objet appelé une tâche future. La tâche elle-même est juste un shell vide qui retourne la valeur si elle a la valeur et si elle ne l'appelle pas wait(). Cela provoque l'opération d'arrêt du thread. Ensuite, l'autre thread appelé par ExecutorService renvoie sa valeur. Et quand il renvoie la valeur, il passe cette valeur dans la FutureTask de telle sorte qu'elle appelle notify() qui redémarre le thread.

La classe ExecutorService effectue le travail. Vous lui avez passé un Callable qu'il a ensuite lancé et appelé. Et retourné une classe de jeton qui fournit l'emplacement futur du résultat. Cette classe renvoie simplement la valeur dont elle dispose ou force la menace qui appelle .get() à arrêter l'opération jusqu'à ce qu'elle puisse renvoyer cette valeur.

Vous pouvez implémenter vous-même la même logique. La JVM est préoccupée par le thread qui place les commandes wait() et notify() en tant que telles mais pas les choses de niveau supérieur comme la classe wrapper fantaisie pour configurer la façon dont le paradigme fonctionne ici. Il y a un certain nombre d'autres choses comme BlockingQueues et AsyncTasks et un tas d'autres classes auxiliaires qui font la logique interne de l'exécution des choses en parallèle. Get a une logique qui dit essentiellement: Si j'ai la valeur, retourne la valeur. Si je n'attends pas. Puis renvoyez la valeur. Et l'astuce est l'autre thread que vous avez lancé lors de l'envoi du code has qui a indiqué mettre la valeur dans cette classe et notifier() tous les threads arrêtés qui attendent sur cet objet.

La JVM ne gère pas cette classe, juste l'exécution des commandes dans plus ou moins bytecode, le JIT le compile un peu plus pendant son exécution. Mais, il n'exécute pas de code source Java, il ne sait pas ce que cette classe est à ce moment-là.Mais, il sait comment faire attendre le thread jusqu'à ce qu'il y ait une valeur, car c'est ce que le code de l'Executor fait réellement, il fait juste un travail utile pour vous parce que gérer le blocage est une tâche fastidieuse.

Il ne s'agit pas de prédire l'avenir. C'est l'endroit où le résultat futur doit être placé. Et si vous l'appelez tôt, votre thread est mis en pause jusqu'à ce que le résultat soit disponible.


Vous comparez la mauvaise chose. Mais non. .get() va "Attendre si nécessaire pour que le calcul se termine, puis récupère son résultat." alors. Ce qui se passe est attendu. C'est juste le résultat est conservé dans la tâche future. Peu importe combien de temps vous dormez le fil principal de votre installation aura toujours le résultat qui vous est donné. C'est juste que vous attendez d'appeler le .get() jusqu'au dernier moment pour pouvoir exécuter les opérations en parallèle.

public class Main { 
    public static void main(String[] args) throws InterruptedException, ExecutionException {   
     ExecutorService exec = Executors.newFixedThreadPool(1); 
     Future<String> f = exec.submit(new TThread()); 
     Thread.sleep(10000); 
     System.out.println("Main Thread "+System.currentTimeMillis());  
     String s = (String) f.get(); 
     System.out.println(s); 
     exec.shutdown(); 
    } 
} 

Vous avez soumis l'unité d'exécution. Dormi pendant 10 secondes. Puis rapporté l'heure. 10 secondes après le début du fil. Vous avez ensuite appelé .get() qui attend si nécessaire mais le TThread est déjà fait, donc il a retourné la réponse.

class TThread implements Callable<String>{ 
    @Override 
    public String call() throws Exception{ 
     Thread.sleep(3000); 
     System.out.println("TThread Thread "+System.currentTimeMillis()); 
     return "something"; 
    } 
} 

Vous avez dormi 3 secondes et sorti le temps 3 secondes après le début du programme. Et retourné quelque chose.

Voici exactement vos résultats. Le point est si vous avez besoin de la réponse au fil que vous appelez obtenir, et il obtiendra la réponse. Mais, si vous pouvez faire d'autres choses pendant un certain temps, puis appelez get, vous avez couru en parallèle pendant ce temps et si vous avez la réponse ou non, vous obtenez le résultat. Mais, il provoque le thread courant à bloquer si ce n'est pas encore fait. Dans ce cas, le résultat a eu la réponse et vient de vous le renvoyer. Mais, si vous renversez les temps. Vous aurez toujours les mêmes choses. Le thread principal signalerait 3 secondes et le TThread signalera 10. Ce que vous devez faire est de signaler l'heure après la commande get. Et dans ce cas, vous verrez 10 pour le principal et 3 pour l'autre. Mais, quand vous changez les temps, vous aurez 3 pour le principal, 7 pour le get, 10 pour l'autre thread.