2017-06-01 2 views
-2

Je veux construire une liste non dupliquée en utilisant ci-dessous le code, mais quelque chose est faux, quelqu'un dit que c'est fil dangereux mais je ne comprends pas, alors s'il vous plaît me donner quelques exemples pour le prouver, MerciComment construire une liste sans duplication?

class BadListHelper <E> { 
    public List<E> list = Collections.synchronizedList(new ArrayList<E>()); 
    public synchronized boolean putIfAbsent(E x) { 
     boolean absent = !list.contains(x); 
     if (absent) 
      list.add(x); 
     return absent; 
    } 
} 
+0

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? –

+1

Pourquoi n'utilisez-vous pas un 'Set'? Plus précisément un 'LinkedHashSet' (Si vous voulez maintenir l'ordre d'insertion)? – anacron

+0

@anacron ça doit être quelque chose comme 'LinkedHashSet' pour assurer l'ordre –

Répondre

2

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; 
    } 
} 
+0

merci mon ami, mais "public putIfAbsent booléen synchronisé (E x)" signifie un autre thread ne peut pas appeler cette méthode jusqu'à ce que la méthode actuelle fait, alors pourquoi il sont des probleams multi-thread? – StarSky

+0

L'appel de méthode 'synchronized' exclut uniquement les autres appels de méthode qui se synchronisent sur' this'. C'est un objet différent de 'list'. Par conséquent, les appels de méthode sur 'list' ne sont pas exclus. –

+0

Je comprends, merci @Stephen C – StarSky