CopyOnWriteArrayListガイド

CopyOnWriteArrayListのガイド

1. 概要

この簡単な記事では、java.util.concurrentパッケージのCopyOnWriteArrayListについて説明します。

これは、明示的な同期なしでスレッドセーフな方法でリストを反復処理する場合に、マルチスレッドプログラムで非常に便利な構造です。

2. CopyOnWriteArrayList API

CopyOnWriteArrayListの設計では、興味深い手法を使用して、同期を必要とせずにスレッドセーフにします。 add()remove() –などの変更方法のいずれかを使用している場合、CopyOnWriteArrayListのコンテンツ全体が新しい内部コピーにコピーされます。

この単純な事実により、we can iterate over the list in a safe way, even when concurrent modification is happening

CopyOnWriteArrayList,iterator()メソッドを呼び出すと、CopyOnWriteArrayListのコンテンツの不変のスナップショットによってバックアップされたIteratorが返されます。

その内容は、Iteratorが作成された時点からArrayList内にあるデータの正確なコピーです。 その間に他のスレッドがリストに要素を追加または削除したとしても、その変更は、そのリストからの以降のデータ検索で使用されるデータの新しいコピーを作成します。

このデータ構造の特性は、修正するよりも頻繁に反復する場合に特に役立ちます。 このシナリオで要素を追加することが一般的な操作である場合、CopyOnWriteArrayListは適切な選択ではありません。コピーを追加すると、パフォーマンスが標準以下になるためです。

3. 挿入中にCopyOnWriteArrayListを反復処理

整数を格納するCopyOnWriteArrayListのインスタンスを作成しているとしましょう。

CopyOnWriteArrayList numbers
  = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 8});

次に、その配列を反復処理するため、Iteratorインスタンスを作成します。

Iterator iterator = numbers.iterator();

Iteratorが作成された後、numbersリストに新しい要素を追加します。

numbers.add(10);

CopyOnWriteArrayList,のイテレーターを作成すると、iterator()が呼び出されたときのリスト内のデータの不変のスナップショットが取得されることに注意してください。

そのため、反復処理中は、反復で10の数は表示されません。

List result = new LinkedList<>();
iterator.forEachRemaining(result::add);

assertThat(result).containsOnly(1, 3, 5, 8);

新しく作成されたIteratorを使用した後続の反復でも、追加された数値10が返されます。

Iterator iterator2 = numbers.iterator();
List result2 = new LinkedList<>();
iterator2.forEachRemaining(result2::add);

assertThat(result2).containsOnly(1, 3, 5, 8, 10);

4. 反復中の削除は許可されていません

CopyOnWriteArrayListは、基になるリストが変更された場合でも要素を安全に反復できるようにするために作成されました。

コピーメカニズムのため、返されたIteratorに対するremove()操作は許可されません–結果としてUnsupportedOperationException:

@Test(expected = UnsupportedOperationException.class)
public void whenIterateOverItAndTryToRemoveElement_thenShouldThrowException() {

    CopyOnWriteArrayList numbers
      = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 8});

    Iterator iterator = numbers.iterator();
    while (iterator.hasNext()) {
        iterator.remove();
    }
}

5. 結論

このクイックチュートリアルでは、java.util.concurrentパッケージからのCopyOnWriteArrayListの実装を確認しました。

このリストの興味深いセマンティクスと、スレッドセーフな方法でリストを反復する方法を見ましたが、他のスレッドは要素の挿入または削除を続行できます。

これらすべての例とコードスニペットの実装は、GitHub projectにあります。これはMavenプロジェクトであるため、そのままインポートして実行するのは簡単です。