29 votes

Est-ce une bonne façon d'utiliser java.util.concurrent.FutureTask ?

Tout d'abord, je dois dire que je suis assez novice dans l'API java.util.concurrent, donc peut-être que ce que je fais est complètement faux.

Qu'est-ce que je veux faire ?

J'ai une application Java qui exécute essentiellement 2 traitements distincts (appelés monPremierProcessus , monSecondProcessus ), mais ces traitements doivent être exécutés en même temps.

Alors, j'ai essayé de le faire :

public void startMyApplication() {
    ExecutorService executor = Executors.newFixedThreadPool(2);
    FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
    FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
    executor.execute(futureOne);
    executor.execute(futureTwo);
    while (!(futureOne.isDone() && futureTwo.isDone())) {
        try {
            // I wait until both processes are finished.
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    logger.info("Processing finished");
    executor.shutdown();
    // Do some processing on results
    ...
}

monPremierProcessus et monSecondProcessus sont des classes qui implémentent Callable<Object> et où tout leur traitement est effectué dans la méthode call().

Cela fonctionne assez bien mais je ne suis pas sûr que ce soit la bonne façon de faire. Y a-t-il une bonne façon de faire ce que je veux ? Si non, pouvez-vous me donner quelques conseils pour améliorer mon code (tout en le gardant aussi simple que possible).

34voto

Yuval Adam Points 59423

Vous feriez mieux d'utiliser le get() méthode.

futureOne.get();
futureTwo.get();

Tous deux attendent la notification du fil d'exécution de la fin du traitement, ce qui vous évite d'utiliser la fonction "busy-wait-with-timer" que vous utilisez actuellement et qui n'est ni efficace ni élégante.

En prime, vous avez l'API get(long timeout, TimeUnit unit) qui vous permet de définir une durée maximale pendant laquelle le thread dormira et attendra une réponse, sinon il continuera à fonctionner.

Voir le [API Java](http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/Future.html#get()) pour plus d'informations.

14voto

cletus Points 276888

La solution de Yuval est bonne. Comme alternative, vous pouvez aussi faire ceci :

ExecutorService executor = Executors.newFixedThreadPool();
FuturTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
FuturTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
executor.execute(futureOne);
executor.execute(futureTwo);
executor.shutdown();
try {
  executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
  // interrupted
}

Quel est l'avantage de cette approche ? Il n'y a pas vraiment de différence, si ce n'est que de cette façon vous empêchez l'exécuteur d'accepter d'autres tâches (vous pouvez aussi le faire de l'autre façon). J'ai tendance à préférer cet idiome à celui-là cependant.

De plus, si l'un des deux get() lève une exception, vous risquez de vous retrouver dans une partie de votre code qui suppose que les deux tâches sont terminées, ce qui pourrait être mauvais.

13voto

seh Points 8533

Les utilisations de FutureTask ci-dessus sont tolérables, mais certainement pas idiomatiques. Vous enveloppez en fait un extra FutureTask autour de celui que vous avez soumis à la ExecutorService . Votre FutureTask est traité comme un Runnable par le ExecutorService . En interne, il enveloppe votre FutureTask -comme- Runnable dans un nouveau FutureTask et vous le renvoie sous la forme d'un Future<?> .

Au lieu de cela, vous devez soumettre votre Callable<Object> à une CompletionService . Vous laissez tomber deux Callable par l'intermédiaire de submit(Callable<V>) puis se retourner et appeler CompletionService#take() deux fois (une fois pour chaque soumission Callable ). Ces appels seront bloqués jusqu'à ce que l'une puis l'autre des tâches soumises soient terminées.

Étant donné que vous avez déjà un Executor en main, construire un nouveau ExecutorCompletionService autour d'elle et y déposer vos tâches. Ne tournez pas et dormez en attendant ; CompletionService#take() bloquera jusqu'à ce que l'une de vos tâches soit terminée (fin d'exécution ou annulation) ou jusqu'à ce que le thread en attente sur take() est interrompu.

4voto

Manish Points 175

Vous pouvez utiliser la méthode invokeall(Colelction....)

    package concurrent.threadPool;

    import java.util.Arrays;
    import java.util.List;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;

    public class InvokeAll {

public static void main(String[] args) throws Exception {
    ExecutorService service = Executors.newFixedThreadPool(5);
    List<Future<java.lang.String>> futureList = service.invokeAll(Arrays.asList(new Task1<String>(),new Task2<String>()));

    System.out.println(futureList.get(1).get());
    System.out.println(futureList.get(0).get());
}

private static class Task1<String> implements Callable<String>{

    @Override
    public String call() throws Exception {
        Thread.sleep(1000 * 10);
        return (String) "1000 * 5";
    }

}

private static class Task2<String> implements Callable<String>{

    @Override
    public String call() throws Exception {
        Thread.sleep(1000 * 2);
        int i=3;
        if(i==3)
            throw new RuntimeException("Its Wrong");
        return (String) "1000 * 2";
    }

}

}

3voto

Louis Jacomet Points 650

Vous pouvez utiliser un Barrière cyclique si vous souhaitez lancer les fils en même temps, ou attendre qu'ils se terminent pour ensuite effectuer d'autres traitements. Voir la javadoc pour plus d'informations.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X