Comprendre les bases des streams parallèles en Java par l’exemple

L’objectif de ce blog sera de vous introduire les bases des streams paralléle Java. On parlera des méthodes parallel() et parallelStream() pour la création des streams parallèles ainsi que de la méthode forEachOrdered pour ordonner le résultat

Comprendre les bases des streams parallèles en Java par l’exemple

Published by: Stephane, Last edited

Dans un article précédent nous avons appris les bases des streams Java et leurs utilités. Utiliser les streams de cette façon, ses éléments sont exécutés en série, c’est à dire l’une à la suite de l’autre.

Il est toutefois possible d’augmenter la performance d'exécution des streams en utilisant les streams parallèles (parallelStream). Dans ce cas plusieurs éléments pourront s'exécuter de façon simultanée.

On verra donc comment tout cela fonctionne dans cet article.

Création des streams parallèles

Ils peuvent être créés de 2 manières, soit en invoquant la méthode parallel() sur un stream existant ou alors en invoquant la méthode parallelStream() des listes (List).


List nombres = Arrays.asList(0,1,2,3,4,5,6,7,8,9);
Stream streamParallel1 =Stream.generate(Math::random).parallel();
Stream streamParallel2 = nombres.parallelStream();

Amélioration de performance avec les streams parallèles

Supposons que nous avons un stream de 1000 éléments et que l’exécution de chaque élément nous prenne 10ms, l’exécution des 1000 éléments nous prendra au total 10000ms (voir un peu plus) soit +/- 10 secondes.

Pour la simulation, j’ai une méthode statique (simulationReseau()) qui va mettre le thread en pause pendant 10ms.


public static int simulationReseau(){
    try{
        Thread.sleep(10);
    }catch (InterruptedException e){
        // catch exception
    }
    return 200;
}

...

public static void main(String... args) {
    long start = System.currentTimeMillis();
    Stream.generate(Math::random)
                    .limit(1000)
                    .forEach(x -> simulationReseau());
    double time = (System.currentTimeMillis() - start)/1000.0;
    System.out.println(time+" secondes");
    // Resultat: 11.285 secondes
}

L’utilisation des streams parallèles rendra cette opération bien plus rapide car plus d’une opération sera exécutée simultanément, d’où la notion de parallélisme. La même opération que précédemment nous prendra donc bien moins de temps. Voyons cela par l’exemple:


public static int simulationReseau(){
    try{
        Thread.sleep(10);
    }catch (InterruptedException e){
        // catch exception
    }
    return 200;
}

...

public static void main(String... args) {
    long start = System.currentTimeMillis();
    Stream.generate(Math::random)
                    .limit(1000)
                    .parallel()
                    .forEach(x -> simulationReseau());

    double time = (System.currentTimeMillis() - start)/1000.0;
    System.out.println(time+" secondes");
    // Resultat: 2.99 secondes
}
    

Cool n’est-ce pas? Nous passons d’une exécution qui prend 11 secondes à une exécution de 3 secondes… énorme gain :)

Il est toutefois à noter qu’avec l’exécution en parallèle on perd l’ordre des éléments du stream. Dans un stream normal qui imprime les éléments d’une liste d'éléments ordonnés 0 à 9, les éléments sont imprimés dans leur ordre croissant.


List nombres = Arrays.asList(0,1,2,3,4,5,6,7,8,9);
nombres.stream().forEach(x->System.out.print(x+","));
// Resultat : 0,1,2,3,4,5,6,7,8,9,

Cela n’est pas le cas dans des streams parallèles où l’ordre d'exécution n’est pas garanti. Exécuter la même opération que précédemment, à plusieurs reprises, produira des résultats différents (l’ordre plus précisément).


List nombres = Arrays.asList(0,1,2,3,4,5,6,7,8,9);
nombres.parallelStream().forEach(System.out::print);
// Resultat1: 6852943107
// Resultat2: 8129047356
// Resultat3: 2435761809

Pour le cas de la méthode terminale forEach, il est possible de retrouver l’ordre des éléments en faisant appel à la méthode forEachOrdered()


List nombres = Arrays.asList(0,1,2,3,4,5,6,7,8,9);
nombres.parallelStream().forEachOrdered(System.out::print);
// Resultat: 0123456789
    

J’espère que cet article vous aidera dans vos projets dans l'amélioration des performances d'exécution de vos opérations.

le gist du contenant les exemples de l'article se trouve ici

Find me on twitter or on

"Je puis tout par Christ qui me fortifie." Phil 4:13