Il est une excellente explication de ce problème par Andrei Pangin, daté par 07 Avril 2015. Il est disponible ici, mais il est écrit en russe (je suggère d'examiner des exemples de code, de toute façon - ils à l'international). Le problème général est un verrou de classe pendant l'initialisation.
Voici quelques citations de l'article:
Selon JLS, chaque classe a un unique initialisation de verrouillage qui est capturé lors de l'initialisation. Lorsque d'autres thread tente d'accéder à cette classe lors de l'initialisation, il sera bloqué sur le verrou jusqu'à ce que l'initialisation est terminée. Lorsque les classes sont initialisés simultanément, il est possible d'obtenir une impasse.
J'ai écris un programme qui calcule la somme des entiers, que devrait-il imprimé?
public class StreamSum {
static final int SUM = IntStream.range(0, 100).parallel().reduce((n, m) -> n + m).getAsInt();
public static void main(String[] args) {
System.out.println(SUM);
}
}
Maintenant, enlevez parallel()
ou remplacer lambda Integer::sum
appelez - ce qui va changer?
Ici, nous voyons de blocage de nouveau [il y a quelques exemples de blocages dans la classe des initialiseurs précédemment dans l'article]. En raison de l' parallel()
flux opérations à exécuter dans un thread séparé de la piscine. Ces threads tentent d'exécuter le corps de lambda, ce qui est écrit dans le bytecode en tant que private static
méthode de StreamSum
classe. Mais cette méthode ne peut pas être exécuté avant la fin de la classe de l'initialiseur statique, qui attend les résultats des cours d'achèvement.
Ce qui est plus mindblowing: ce code fonctionne différemment dans les différents environnements. Il fonctionne correctement sur un seul PROCESSEUR de la machine et sera plus susceptible de se bloquer sur un multi CPU de la machine. Cette différence provient du fait que le Fork-Join piscine de mise en œuvre. Vous pouvez le vérifier vous-même en changeant le paramètre -Djava.util.concurrent.ForkJoinPool.common.parallelism=N