23

J2EE Web アプリケーションの開始時に一度だけコレクションを埋めます。次に、複数のスレッドが同時にアクセスする可能性がありますが、読み取り専用です。

並列書き込みには同期コレクションの使用が必須であることは知っていますが、並列読み取りにはまだ必要ですか?

4

5 に答える 5

20

この場合、コレクションの内部状態を変更していないため、通常はいいえ。コレクションを反復処理すると、反復子の新しいインスタンスが作成され、反復の状態は反復子インスタンスごとになります。


余談:読み取り専用のコレクションを保持することは、コレクション自体への変更を防止するだけであることに注意してください。各コレクション要素は引き続き変更可能です。

class Test {
    public Test(final int a, final int b) {
        this.a = a;
        this.b = b;
    }

    public int a;
    public int b;
}

public class Main {

    public static void main(String[] args) throws Exception {
        List<Test> values = new ArrayList<Test>(2);
        values.add(new Test(1, 2));
        values.add(new Test(3, 4));

        List<Test> readOnly = Collections.unmodifiableList(values);
        for (Test t : readOnly) {
            t.a = 5;
        }

        for (Test t : values) {
            System.out.println(t.a);
        }
    }

}

これは以下を出力します:

5
5

@WMR アンサーからの重要な考慮事項。

コレクションを読み込んでいるスレッドが、コレクションを埋める前または後に開始されるかによって異なります。埋める前にそれらが開始された場合、これらのスレッドが更新された値を参照するという保証はありません (同期なしでは)。

この理由は Java メモリ モデルにあります。詳しく知りたい場合は、次のリンクの「可視性」セクションをお読みください: http://gee.cs.oswego.edu/dl/cpj/jmm.html

また、コレクションがいっぱいになった後にスレッドが開始されたとしても、コレクションの実装が読み取り操作でも内部状態を変更する可能性があるため、同期する必要がある場合があります ( Michael Bar-Sinaiに感謝します。そのようなコレクションが存在することは知りませんでした)。

オブジェクトのパブリッシング、可視性などのトピックをより詳細にカバーする並行性のトピックに関するもう 1 つの非常に興味深い読み物は、Brian Goetz の著書Java Concurrency in Practiceです。

于 2008-09-30T09:18:59.603 に答える
10

一般的なケースでは、そうすべきです。これは、一部のコレクションが読み取り中に内部構造を変更するためです。アクセス順序を使用する LinkedHashMap が良い例です。しかし、私の言葉を鵜呑みにしないでください。

アクセス順のリンク付きハッシュ マップでは、get を使用してマップをクエリするだけで、構造的な変更が行われ ます リンク付きハッシュ マップの javadoc

キャッシュ、コレクション統計、最適化、おかしなものがまったくないことが絶対に確実な場合は、同期する必要はありません。その場合、コレクションに型制約を設定します。コレクションを Map (LinkedHashMap を許可する) として宣言しないでください。ただし、HashMap (純粋主義者の場合、HashMap の最終サブクラスですが、それも取る可能性があります) として宣言しないでください。遠い...)。

于 2008-09-30T09:51:04.567 に答える
10

コレクションを読み込んでいるスレッドが、コレクションを埋める前または後に開始されるかによって異なります。埋める前にそれらが開始された場合、これらのスレッドが更新された値を参照するという保証はありません (同期なしでは)。

この理由は Java メモリ モデルにあります。詳しく知りたい場合は、次のリンクの「可視性」セクションをお読みください: http://gee.cs.oswego.edu/dl/cpj/jmm.html

また、コレクションがいっぱいになった後にスレッドが開始されたとしても、コレクションの実装が読み取り操作でも内部状態を変更する可能性があるため、同期する必要がある場合があります ( Michael Bar-Sinaiに感謝します。標準の JDK にそのようなコレクションが存在することは知りませんでした)。 )。

オブジェクトのパブリッシング、可視性などのトピックをより詳細にカバーする並行性のトピックに関するもう 1 つの非常に興味深い読み物は、Brian Goetz の著書Java Concurrency in Practiceです。

于 2008-09-30T09:36:52.197 に答える
3

他の回答で説明されているように、そうする必要はありません。コレクションを読み取り専用にしたい場合は、次を使用できます。

yourCollection = Collections.unmodifableCollection(yourCollection);

(List、Set、Map およびその他のコレクション型にも同様のメソッドが存在します)

于 2008-09-30T09:28:24.527 に答える
0

コレクション自体はそうではありませんが、それが保持するものも不変でない場合、それらの個別のクラスには独自の同期が必要であることに注意してください。

于 2008-09-30T09:21:19.193 に答える