Qu'est-ce qu'un ExecutorService?

Pour être honnête, cette question n'est pas trop nouvelle. Plus de 13 ans se sont écoulés depuis la sortie de Java 5 et du package java.util.concurrent. *, Mais au cours de mes dix années de pratique, je n'ai jamais eu à affronter cette bête. Néanmoins, on m'a posé cette question à plusieurs reprises lors d'entretiens et j'ai dû faire connaissance.



Naturellement, la première chose avec laquelle j'ai commencé est Habr. Mais, malheureusement, je n'ai trouvé que deux articles ici:



habrahabr.ru/post/260953

habrahabr.ru/post/116363



Le premier, évidemment, pour ceux qui comprennent et ont de l'expérience avec ExecutorService. Le second, malheureusement, ne m'est pas entré. Il semble petit et "sur le cas", mais après avoir relu plusieurs fois je ne comprends toujours pas ce qu'est ExecutorService et avec quoi il est mangé. J'ai donc dû m'asseoir à Eclipse, fumer, lire javadoc et comprendre.



Jetons donc un œil à un exemple simple:



ExecutorService service = Executors.newFixedThreadPool(3);
service.execute(new Runnable() {
    public void run() {
        System.out.println("Another thread was executed");
    }
});

      
      





Dans cet exemple, nous avons créé l'objet ExecutorService lui-même et avons appelé la méthode execute dessus. En lui passant l'implémentation de thread la plus courante. Tout cela aurait pu être construit à la manière du vieux grand-père, mais cela, voyez-vous, est beaucoup plus simple et élégant. En fait, nous avons rapidement dérivé un autre thread asynchrone du thread actuel, qui peut y faire quelque chose en arrière-plan.

Nous avons créé l'objet ExecutorService à l'aide d'une fabrique. Ses méthodes sont assez évidentes, nous ne serons donc pas trop tergiversants. En voici quelques-uns:



ExecutorService service1 = Executors.newSingleThreadExecutor();
ExecutorService service2 = Executors.newFixedThreadPool(3);
ExecutorService service3 = Executors.newScheduledThreadPool(3);

      
      





En plus de la méthode execute, qui est appelée sur le principe "fire and forget", notre service dispose également d'une méthode submit. La seule différence avec la première est que la seconde renvoie un objet de l'interface Future. C'est juste un excellent moyen de contrôler l'état du thread que nous exécutons en arrière-plan. C'est fait quelque chose comme ceci:



Future future = service.submit(new Runnable() {
    public void run() {
        System.out.println("Another thread was executed");
    }
});
...
future.get();

      
      





Notez que la méthode get bloquera mortellement le thread actuel et attendra la fin du thread d'arrière-plan. Vous n'avez plus besoin de toutes ces jointures non évidentes! Si nous avons peur que notre thread d'arrière-plan ne se termine jamais, nous pouvons utiliser get (long, TimeUnit).



Parfois, vous devez renvoyer les données d'un thread d'arrière-plan vers le thread actuel. La méthode submit nous aidera également avec cela, mais maintenant nous devons lui passer non pas un objet Runnable, mais un objet Callable. En fait, ce sont deux interfaces identiques, la seule différence est que cette dernière peut renvoyer quelque chose:



Future future = service.submit(new Callable(){
    public Object call() throws Exception {
        System.out.println("Another thread was executed");
        return "result";
    }
});
...
System.out.println("Result: " + future.get());

      
      





Bref, c'est tout. Il y avait des méthodes de création d'ExecutorService - et dans l'usine (il y en a beaucoup), étaient les méthodes de l'ExecutorService, des problèmes de gestion des erreurs dans un thread d'arrière-plan, mais c'est une autre histoire ...



À la fin de n'oubliez pas de:



servcie.shutdown();

      
      





Ou pas, si tous les threads d'arrière-plan sont des démons.



All Articles