2016-12-20 24 views
5

私はListが複数のスレッドによって呼び出される必要があります。一人の作家しかいなくなり、この作家は要素を追加して何もしません。要素は決して削除または変更されません。多くの同時リーダスレッドはlist.size()list.get(index)を呼び出します。ArrayListがシングルライターマルチリーダーシステムで失敗する可能性はありますか?

場合によっては、要素が追加されると内部配列が大きくなる必要があることを前提としています。

普通のArrayListを使用して手放すことができますか、または例外を回避するためにいくつかの並行構造を実装する必要がありますか?

+3

「素晴らしい並列構造」... [ReadWriteLock'](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReadWriteLock.html)です。実装するのは些細なことです。 – aioobe

+3

オブジェクトに変異がありますか?チック。それは複数のスレッドによってアクセスされますか?チック。オブジェクトにスレッドセーフの保証が付いていますか?いいえ、おめでとうございます。何らかの同期が必要です。チェックリストは本当に簡単です。 – biziclop

+0

広告掲載オーダーは気になりますか? – Makoto

答えて

-1

私の提案は、あなたの読者はすべて意味、Listのと同じ状態を読んでする必要がある場合は、任意の瞬間

+2

セマフォはここでは正しい選択ではないようです。私は['ReadWriteLock'](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReadWriteLock.html)に行きます。 – aioobe

3

でリストを読んでいる読者制御することができ、そのように、任意の例外を回避するためにセマフォを使用することです作者が読んでいるすべての読者の間に書くことができないとしたら、ReadWriteLockまたはこの場合を扱うもっと複雑な方法を使用する必要があります。

メモリ使用量を惜しまない場合は、読者が常に最新のデータを入手できるかどうかは気にしないでください。唯一の目的は例外を避けることです。例外を避けるために作られたCopyOnWriteArrayList書き込み/反復の競合。このListに書き込むと、最終結果として新しいものが作成されますが、現在Listを使用している読者は、「古い」(書き換え前の)読み込みを続行します。

+3

私は 'CopyOnWriteArrayList'を使って要素を追加するのはかなりコストがかかりますし、要素が増えるほど高価になります(バッキング配列全体を毎回新しい場所にコピーする必要があるため)、 'size()'と 'get()'は安価です。通常の並行構造では、add()、size()、get()の間に余分なコストが分散します。あなたのリストがまれにしか変更されていないのにたくさんの質問があった場合は、コピーオンライトを行ってください。それ以外はしないでください。 – biziclop

+0

@biziclop素敵な要約、私はそれを含めるべきだった。 'CopyOnWriteArrayList'はあなたがめったに書くことがなく、しばしば読むことができない状況に最適です。 – Zircon

+0

あなたはそうです、ロックやコピーオンライトスキームが必要です。この分析では、作者が1人だけの場合、古い同期がうまく動作することがわかります。http://blog.takipi.com/java-8-stampedlocks-vs-readwritelocks-and-synchronized/ – ccleve

1

まず、ArrayListのドキュメントはスレッドセーフではないと言います。そのような場合には使用しないことをお勧めします。さらに、理論的にはうまくいきましたが、このプログラムを管理している人は誰でも読んだり書いたりした厳しい制約を覚えています。それはあなたの質問が理論的な観点から本当に面白いので、私は見てみると思った。

最初に注意しなければならないのは、読者がイテレータを使用してリストにアクセスしようとしたとき、遅かれ早かれ、ConcurrentModificationExceptionで読み込みが失敗することです。これは、リストへのすべての変更が追跡され、イテレータがイテレータの外で修正を検出した場合には高速で失敗するためです。

これは、オリジナルの質問にイテレータを使用する必要性を指定していないため、get()およびsize()を追加して読み込むだけで自分自身を制限するとどうなりますか?この場合、内部的にリストに現在のサイズがバッキングデータ配列とともに格納されていて、どちらの変数もvolatileとしてマークされていないため、時折エラーが発生することがあります。その結果、(Javaのメモリ保証のために)データがまだ更新されていない間に読者のスレッドの1つが更新されたサイズの変数を見ることが理論的には可能だと思います。この場合、返される迷惑データや、indexOutOfBoundsを終了させる可能性があります。

+0

はい、正確です。 JVMは命令を並べ替えることができ、記述した正確な問題を発生させることができます。 – ccleve

1

サードパーティのライブラリを使用している場合は、Eclipse Collectionsには複数のリーダーと1つのライターをサポートするMultiReaderFastListというタイプがあります。それはiteratorにスローされます。反復処理が必要な場合は、多くの内部反復メソッドの1つを使用して、読み取りロックを安全に取得し、繰り返しが完了したら解放します。命令型反復コードを記述する必要がある場合は、withReadLockAndDelegatewithWriteLockAndDelegateという2つのメソッドがあり、コードブロックの前後に安全にロックすることができます(iterator)。

注:私はEclipse Collectionsのコミッタです。

関連する問題