One problème avec votre code est que les opérations sur votre objet (exposé) list
, et la méthode putIfAbsent
se synchronisent sur différents objets. Cela signifie que putIfAbsent
a une condition de concurrence en ce qui concerne les opérations directes sur list
.
Par exemple, si vous avez deux fils:
- thread A appelle
helper.list.add(e)
- appelle fil B
helper.putIfAbsent(e)
alors vous pourriez finir avec e
dans la liste deux fois ... en fonction sur le timing. Maintenant
public synchronized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
// <<--- the Thread A call could happen here.
if (absent) {
list.add(x);
}
return absent;
}
il est vrai, vous obtiendrez le même effet si les discussions A et B à la fois appelé helper.list.add
directement. Cependant, la sémantique implicite de putIfAbsent
est qu'il n'ajoutera pas un élément qui est déjà là ... et c'est ce qu'il ferait dans le cas ci-dessus. En effet, l'implémentation de Collections.synchronizedList
renvoie un objet List
qui se synchronise sur lui-même. Donc, une solution est de changer putIfAbsent
à ceci:
public boolean putIfAbsent(E x) {
synchronized (list) {
boolean absent = !list.contains(x);
if (absent) {
list.add(x);
}
return absent;
}
}
Qu'est-ce que c'est «Liste non dupliquée»? Qu'est-ce que "quelque chose ne va pas"? Quel est le comportement attendu et réel? –
Pourquoi n'utilisez-vous pas un 'Set'? Plus précisément un 'LinkedHashSet' (Si vous voulez maintenir l'ordre d'insertion)? – anacron
@anacron ça doit être quelque chose comme 'LinkedHashSet' pour assurer l'ordre –