Étrange comportement de code concurrent

Je suis en train d’apprendre la concurrence de Java. Et je suis très surpris par la façon dont le code suivant se comporte.

import java.util.concurrent.*; public class Exercise { static int counter = 0; static synchronized int getAndIncrement() { return counter++; } static class Improper implements Runnable { @Override public void run() { for (int i = 0; i < 300; i++) { getAndIncrement(); } } } public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0; i < 300; i++) { executorService.submit(new Improper()); } executorService.shutdown(); System.out.println(counter); } } 

Ne devrait-il pas sortir 90000 tout le temps? Au lieu de cela, le résultat diffère tout le temps.

  1. executorService.shutdown() n’attend pas la fermeture du service. Vous avez besoin d’un appel pour awaitTermination .

  2. vous accédez au counter partir de la méthode principale sans locking. Je pense que vous échapperiez de peu à une course de données si vous attendiez l’arrêt du service de l’exécuteur, mais sachez qu’en règle générale, vous devez synchroniser tous les access d’une variable partagée, pas seulement les écritures, pour que la visibilité soit garantie. Modèle de mémoire Java.

Vous devez vous assurer que toutes les tâches ont eu le temps de se terminer. Utiliser awaitTermination

 public static void main(Ssortingng[] args) throws InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0; i < 300; i++) { executorService.submit(new Improper()); } executorService.shutdown(); executorService.awaitTermination(2, TimeUnit.SECONDS); System.out.println(counter); } 

Vous n’attendez pas que toutes vos tâches soumises soient terminées, voir le document javadoc pour ExecutorService.html # shutdown . Obtenir une sortie arbitraire à chaque fois est donc le résultat attendu.