59

のOpenJDK ソース コードを調べたCopyOnWriteArrayListところ、すべての書き込み操作は同じロックで保護されており、読み取り操作はまったく保護されていないようです。私が理解しているように、JMM では、変数へのすべてのアクセス (読み取りと書き込みの両方) をロックで保護する必要があります。そうしないと、並べ替え効果が発生する可能性があります。

たとえば、set(int, E)メソッドには次の行が含まれています (ロックされています)。

/* 1 */ int len = elements.length;
/* 2 */ Object[] newElements = Arrays.copyOf(elements, len);
/* 3 */ newElements[index] = element;
/* 4 */ setArray(newElements);

一方get(int)、メソッドはreturn get(getArray(), index);.

JMM についての私の理解では、これは、getステートメント 1 ~ 4 が 1-2(new)-4-2(copyOf)-3 のように並べ替えられると、配列が矛盾した状態になる可能性があることを意味します。

CopyOnWriteArrayListJMM の理解が間違っているのでしょうか、それともスレッドセーフである理由について他に何か説明はありますか?

4

4 に答える 4

73

基になる配列参照を見ると、 としてマークされていることがわかりますvolatile。書き込み操作が発生すると (上記の抜粋のように)、このvolatile参照は を介し​​て最終ステートメントでのみ更新されsetArrayます。この時点まで、すべての読み取り操作は配列の古いコピーから要素を返します。

重要な点は、配列の更新はアトミック操作であるため、読み取りでは常に一貫した状態の配列が表示されることです。

書き込み操作のロックのみを取得する利点は、読み取りのスループットが向上することです。これは、リスト全体のコピーを伴うため、 a の書き込み操作CopyOnWriteArrayListが非常に遅くなる可能性があるためです。

于 2010-06-01T15:05:34.047 に答える
19

配列参照の取得はアトミック操作です。したがって、読者は古い配列または新しい配列のいずれかを見ることができます - どちらの方法でも状態は一貫しています。(set(int,E)参照を設定する前に新しい配列の内容を計算するため、割り当てが行われたときに配列は一貫しています。)

配列参照自体はvolatile、リーダーがロックを使用して参照配列への変更を確認する必要がないように、 としてマークされています。(編集: また、volatile割り当てが再順序付けされないことを保証します。これにより、配列が矛盾した状態にある可能性がある場合に割り当てが行われます。)

書き込みロックは、同時変更を防止するために必要です。これにより、配列が一貫性のないデータを保持したり、変更が失われたりする可能性があります。

于 2010-06-01T15:05:14.340 に答える
-2

CopyOnWriteArrayListは、Java 5 Concurrency API で導入された並行 Collection クラスであり、Java で人気のあるクラスConcurrentHashMapです。

CopyOnWriteArrayListのような List インターフェイスを実装しますがArrayList、これはスレッド セーフなコレクションであり、Vector や他のスレッド セーフなコレクション クラスとは少し異なる方法でスレッド セーフを実現します。VectorLinkedList

名前が示すように、CopyOnWriteArrayList は、追加や設定などのミューテーション操作ごとに、基礎となる ArrayList のコピーを作成します。通常、CopyOnWriteArrayList は、すべての書き込み操作でコストのかかる配列コピーを伴うため、非常にコストがかかりますが、反復が数を超えるリストがある場合は非常に効率的です。

CopyOnWriteArrayList の Iterator はフェイルセーフであり、Iterator は ArrayList の別のコピーで動作するため、Iteration が開始されると基になる CopyOnWriteArrayList が変更されても ConcurrentModificationException をスローしません。したがって、CopyOnWriteArrayList で行われたすべての更新は Iterator では利用できません。

最新バージョンを取得するには、次のように新しい読み取りを行いますlist.iterator();

そうは言っても、このコレクションを頻繁に更新すると、パフォーマンスが低下します。a を並べ替えようとするとCopyOnWriteArrayList、リストが an をスローすることがわかりますUnsupportedOperationException(並べ替えは、コレクションの set を N 回呼び出します)。この読み取りは、90% 以上の読み取りを行っている場合にのみ使用してください。

于 2020-10-25T13:49:08.627 に答える