Au travail, j'ai reçu la consultation suivante. "Lorsqu'un traitement parallèle est effectué à l'aide de Completable Future, la valeur peut ne pas être renvoyée correctement environ une fois toutes les dizaines à centaines de fois."
Prenez note de la réponse à ce moment-là.
L'environnement d'exécution est
est.
Il existe les processus suivants.
Les informations sont stockées dans summaryItem en parallèle à l'aide de CompletableFuture.
xxxAPI.search (nombre);
appelle une API externe pour obtenir des informations sur l'élément.
Où se cachent les insectes?
public List<Item> search(int length) {
//Variables contenant les résultats de la recherche
List<Item> summaryItem = new ArrayList<>(length);
List<CompletableFuture<Void>> futures = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
final int number = i;
CompletableFuture<Void> f = CompletableFuture.runAsync(() -> {
//Obtenez des données en appelant une API externe
Item item = xxxAPI.search(number);
summaryItem.add(item);
}, pool);
futures.add(f);
}
//Attendez que tous les traitements parallèles soient terminés
CompletableFuture<Void> all =
CompletableFuture.allOf(futures.toArray(new CompletableFuture[length]));
all.join();
return summaryItem;
}
C'est la partie du traitement suivant.
summaryItem.add(item);
summaryItem est java.util.ArrayList. ArrayList n'est pas ** thread-safe. ** ** Jetons un coup d'œil à JavaDoc.
https://docs.oracle.com/javase/jp/8/docs/api/java/util/ArrayList.html
** Cette implémentation n'est pas synchronisée. ** Si plusieurs threads accèdent à une instance ArrayList en parallèle et qu'au moins un de ces threads modifie structurellement la liste, elle doit être synchronisée en externe. Les modifications structurelles sont le processus d'ajout ou de suppression d'un ou plusieurs éléments, ou le redimensionnement explicite du tableau sous-jacent. Le processus consistant à définir uniquement la valeur d'un élément n'est pas un changement structurel. Ceci est généralement réalisé en synchronisant avec certains objets qui encapsulent naturellement la liste. Si un tel objet n'existe pas, essayez de «envelopper» la liste à l'aide de la méthode Collections.synchronizedList. Il est recommandé de le faire au moment de la création pour éviter tout accès accidentel à la liste sans être synchronisée.
Vous l'avez écrit fermement.
Utilisez une liste thread-safe. Ou, passez en premier lieu au traitement thread-safe. Nous avons répondu avec une approche sous la forme de.
Le paquet java.util.concurrent
a une liste bien synchronisée. Utilisons ceci.
List<Item> summaryItem = new ArrayList<>(length);
Changez simplement le code ci-dessus comme ci-dessous et cela fonctionnera normalement.
List<Item> summaryItem = new CopyOnWriteArrayList<>(length);
Dans certains cas, l'utilisation de Collections.synchronizedList
résoudra le problème, mais si vous utilisez iterator, vous devrez toujours vous synchroniser.
En premier lieu, vous devez renvoyer la valeur au format java.util.function.Supplier.
public List<Item> search(int length) {
//Processus d'exécution parallèle de la recherche
List<CompletableFuture<Item>> futures = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
final int number = i;
CompletableFuture<Item> f = CompletableFuture.supplyAsync(() -> {
//Obtenez des données en appelant une API externe
return xxxAPI.search(number);
}, pool);
futures.add(f);
}
//Collection de résultats de recherche
try {
List<Item> summaryItem = new ArrayList<>();
for (CompletableFuture<Item> f : futures) {
summaryItem.add(f.get());
}
return summaryItem;
} catch (ExecutionException | InterruptedException ex) {
throw new RuntimeException(ex);//Je l'ai écrit de manière appropriée.
}
}
Cela se produit extrêmement accidentellement, et le fait est qu'il est difficile de trouver un problème sans une certaine connaissance de l'API Java. Le code de test passera également (approximativement).
Je pense qu'il est plus utile de faire une revue de code pour ces parties que de déboguer.
Vous ne pouvez probablement pas le trouver avec CheckStyle, mais j'ai l'impression que vous pouvez trouver le problème avec un outil d'analyse de code statique. Je le chercherai dans le futur.
Recommended Posts