2017-06-01 9 views
-2

私はコードの下に使用して重複しないリストを作成したいのですが、何かが間違っている、誰かがそれはスレッド安全ではないと言うが、私はそう私にそれを証明するためにいくつかの例を与えてください、それを得ることはありません、おかげ重複しないリストを作成するには?

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

「複製されていないリスト」とは何ですか? 「何か間違っている」とは何ですか?期待される、実際の行動は何ですか? –

+1

なぜ 'Set'を使わないのですか?より具体的には 'LinkedHashSet'(挿入順序を維持したい場合)ですか?それ@anacron – anacron

+0

は、問題がどこにあるか私は理解していないため –

答えて

2

ワンあなたのコードの問題は、(公開された)listオブジェクトとputIfAbsentメソッドの操作が、異なるオブジェクトで同期していることです。つまり、の直接操作に関しては、putIfAbsentに競合条件があります。例えば

次の2つのスレッドがある場合:

  • スレッドAがhelper.list.add(e)
  • スレッドBがhelper.putIfAbsent(e)

を呼び出して、あなたが二回リストにeで終わる可能性が...によって呼び出されますがタイミング。スレッドAとBの両方が直接helper.list.addを呼び出した場合

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

今確かに、あなたは同じ効果を得るでしょう。しかし、putIfAbsentという暗黙の意味は、既に存在する要素を追加しないことであり、それが上記の場合に行うことです。

実際、Collections.synchronizedListの実装では、それ自体で同期するListオブジェクトが返されます。したがって、putIfAbsentを次のように変更してください。

public boolean putIfAbsent(E x) { 
    synchronized (list) { 
     boolean absent = !list.contains(x); 
     if (absent) { 
      list.add(x); 
     } 
     return absent; 
    } 
} 
+0

おかげで私の友人を見ますが、「公共同期ブールputIfAbsent(E x)が」現在のメソッドが完了するまで、別のスレッドがこのメソッドを呼び出すことはできませんを意味し、なぜそこにマルチスレッドのプロブルムですか? – StarSky

+0

'synchronized'メソッド呼び出しは' this'で同期する他のメソッド呼び出しだけを除外します。それは 'list'とは異なる目的です。したがって、 'list'のメソッド呼び出しは除外されません。 –

+0

私は理解しています、ありがとう@Stephen C – StarSky

関連する問題