2016-12-29 4 views
1

スレッドセーフなコレクション(リストまたはセット)を同期したいと思っています。私のコレクションには、名前、値などのfiledsを持つVariableクラスのオブジェクトが含まれます。今度は、2つのスレッドがそのリストからオブジェクトを変更したい(新しいものを追加するか、既存のものを削除する)とします。それを行うには最高の解決策は何ですか?私はstackoverflowでこのようなsmthを見つけました。同期された変数のコレクション

List<Variable> varList = Collections.synchronizedList(new ArrayList<Variable>()); 

void processList(List<Variable> varList, String name) 
{ 
    synchronized(varList) { 
     varList.stream().filter(o -> o.getName().equals(name)).findFirst().ifPresent(o -> o.setValue(100)); 
    // or 
    varList.remove(0); 
    // or 
    varList.add(new Variable()); 
    } 
} 

問題なく動作しますか?または、より良い解決策がありますか?

+0

スレッドがリスト内の 'Variable'オブジェクトを変更する場合、' Variable'クラスはリストではなくスレッドセーフでなければなりません。 –

+0

それは動作します、リストはスレッドセーフになります。 –

+0

Variable.classが変更可能な場合、このオブジェクトには、別の「競合状態」につながる状態を共有しています。 –

答えて

2

最適な解決策は、使用例によって異なります。注文したrandomacces Collectionが必要な場合は、3つのオプションがあります。

1)あなたが説明したもの(Synchronized List)。それは、全体のコレクションがスレッドアクセスでロックされるdrawBackを使用して、下位のコレクション型と同じ速さで実行されます。

Iterator以外の実装では、デフォルトでThreadsafeではないため、余分なthreadSaftyメジャーが必要です。 (イテレータは、しばしばforEach, clearと他のデフォルト(java 8)実装で使用されます)。

2)CopyOnWriteArrayList。これは、JDKにある唯一のThreadSafe(スナップショット機能を持つIteratorを含む)randomAccesリストです。

欠点は、データの追加/削除時にデータをコピーすることです。小規模なコレクションでは問題にはならないかもしれませんが、基本ルールは「書き込み」アクションよりも「読み取り」アクションがかなり多い場合に最適なソリューションです。

3)Vector。これは、 'write'メカニズムがバッキングデータのコピーを作成する必要はありませんが、CopyOnWriteListにコンパイルされた 'read'アクションでは少し遅くなります。

この欠点は、IteratorもThreadSafeではないことです。同期リストとの違いは、コレクションにアクセスするスレッドがかなり多い場合、SynchronizedListよりも優れたパフォーマンスを発揮することです。



Sumarry:accesingスレッドの数が少ない

Synchronized List

  • 高速パフォーマンス。
  • イテレータは、「読み取り」アクション
  • コピー「書き込み」アクションのバッキング・データを(大きなコレクションに問題があるかもしれません)上の安全

CopyOnWriteArrayList

  • 高速パフォーマンスをスレッドされていません
  • イテレータはスレッドセーフです(スナップショット機能を使用)
accesingスレッドの数が多い(同期リストへcompaired)

Vector

  • 高速性能。
  • イテレータは、私はそれがはるかに簡単なんでしょう
+0

答えをありがとう。私の場合、performaceはそれほど重要ではないので、私はprollyリストにとどまります。しかし、私はいくつかの問題があるようです:1.そのリスト内のオブジェクトを変更/変更したい(フィールドを変更する、新しい要素を削除/追加するだけでなく)。私の質問の下の他のコメントによると、私はいくつかの特別な保護が必要ですか?どのように私はそれを行うことができますか? 2.イテレータ操作はListに対してスレッドセーフではないと言っています。だから、stream()とfilter()メソッドを使ってオブジェクトを見つけるのは良い考えではありませんか? 3.私は実際に同期リストを必要としますか、私はちょうど同期ブロックに "通常の"リストを置くべきですか? –

+1

1)「変数」の同期は、オブジェクト自体の内部で処理する必要があります。 Listの実装でこれを行う方法はありません。この問題を最初に解決してください。2)イテレータを使用する場合、最も簡単な方法は、リストをロックオブジェクトとして使用して同期ブロックを使用することです。 (例えば、 'synchronized(varList){/ * do stuff * /}')。 3)あなたがリストにアクセスするたびに同期ブロックを使用することを確かめたならば、それ(そして前の部分)は問題にならないでしょう。パフォーマンスが問題ではない場合は、代わりにCopyOnWriteArrayListを使用してください。完全にThreadSafeなので(もちろんObject自体は除きます!) – n247s

+0

お返事ありがとうございました。今、それは私のためのより明確な方法です。しかしもう一つ質問があります。私が変更したいオブジェクトが常に同じリストから取得される場合、本当に単一のVariableインスタンスに対して別の保護が必要ですか?オブジェクトを取得するためには、別のスレッドがリスト上で作業している場合にスレッドがsynchronized(varList)文を渡す必要があります。だから私は余分な同期のメカニズムは、私はリストの外にそれらのオブジェクトにアクセスしたい場合にのみ必要と仮定か、間違っている? –

1

スレッドセーフではありません。同期と同じリストオブジェクトを使用して、同期ブロック内のリストのすべての変更を行うだけで済みます。

たとえば、あなたがリストすでに変数のオブジェクトで満たさ

List<Variable> varList; 

があるとしましょう。さて、毎回あなたがアクセスしたり、あなたのコード内でこのリストを変更したい、次のように、変数リストは上の同期ブロックのロックとのコードの一部をラップする必要があります。このため

synchronized(varList) { 
     // here you access, add, delete, or modify the List 
     // for example: 
     if(varList.get(1) == 3) { 
      varList.remove(1); 
      varList.add(new Variable()); 
     } 
    } 

をあなたは確認する必要があり、その2スレッドはと同じ varListで動作しています。これは、varListは両方のスレッド(フィールドや静的変数など)の共有変数です。また、他の新しいオブジェクトをロックとして使用することもできますが、常に両方のスレッドの共通オブジェクトであることを確認してください(メモリ内の異なるオブジェクトを参照する同じ名前の2つの変数ではありません)。 しかし、私はロックとしてそのリスト自体を使うほうが好きです。そうすれば、コードがわかりやすくなります。そのブロックの中で私はそのリストを変更しています。

関連する問題