1. 序章

この記事では、Fail-FastおよびFail-Safe Iteratorsの概念を紹介します。

Fail-Fastシステムは、可能な限り高速に操作を中止し、障害を即座に公開して、操作全体を停止します。

一方、 フェイルセーフシステムは、障害が発生した場合に操作を中止しません。 このようなシステムは、障害の発生を可能な限り回避しようとします。

2. フェイルファストイテレータ

Javaのフェイルファストイテレータは、基になるコレクションが変更されたときに再生されません。

コレクションは、modCountと呼ばれる内部カウンターを維持します。 コレクションにアイテムが追加または削除されるたびに、このカウンターは増分されます。

反復するとき、 next()呼び出しごとに、modCountの現在の値が初期値と比較されます。 不一致がある場合は、 ConcurrentModificationException がスローされ、操作全体が中止されます。

java.utilパッケージコレクションのデフォルトのイテレータ( ArrayList HashMap など)。 フェイルファストです。

ArrayList<Integer> numbers = // ...

Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
    Integer number = iterator.next();
    numbers.add(50);
}

上記のコードスニペットでは、 ConcurrentModificationException は、変更が実行された後の次の反復サイクルの開始時にスローされます。

フェイルファスト動作は、同時変更の場合の動作を予測することが不可能であるため、すべてのシナリオで発生することが保証されているわけではありません。これらのイテレーターは、ベストエフォートベースでConcurrentModificationExceptionをスローします

Collection の反復中に、アイテムがIteratorのremove()メソッドを使用して削除された場合、それは完全に安全であり、例外をスローしません。

ただし、 Collectionremove()メソッドを使用して要素を削除すると、例外がスローされます。

ArrayList<Integer> numbers = // ...

Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
    if (iterator.next() == 30) {
        iterator.remove(); // ok!
    }
}

iterator = numbers.iterator();
while (iterator.hasNext()) {
    if (iterator.next() == 40) {
        numbers.remove(2); // exception
    }
}

3. フェイルセーフイテレータ

フェイルセーフイテレータは、例外処理の不便さよりも障害がないことを優先します。

これらのイテレータは、実際のコレクションのクローンを作成し、それを反復処理します。 イテレータの作成後に変更が発生した場合でも、コピーは変更されません。 したがって、これらのイテレータは、変更された場合でもコレクションをループし続けます。

ただし、真にフェイルセーフなイテレータのようなものは存在しないことを覚えておくことが重要です。 正しい用語は弱一貫性です。

つまり、 a Collectionが反復中に変更された場合、イテレーターが認識するものは弱く保証されます。 この動作は、コレクションごとに異なる可能性があり、そのような各コレクションのJavadocに記載されています。

ただし、フェイルセーフイテレータにはいくつかの欠点があります。 1つの欠点は、 Iteratorが実際のコレクションではなくクローンで動作しているため、コレクションから更新されたデータを返すことが保証されないことです。

もう1つの欠点は、時間とメモリの両方に関して、コレクションのコピーを作成するオーバーヘッドです。

ConcurrentHashMap CopyOnWriteArrayListなどのjava.util.concurrentパッケージのCollectionsIterators。 本質的にフェイルセーフです。

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

map.put("First", 10);
map.put("Second", 20);
map.put("Third", 30);
map.put("Fourth", 40);

Iterator<String> iterator = map.keySet().iterator();

while (iterator.hasNext()) {
    String key = iterator.next();
    map.put("Fifth", 50);
}

上記のコードスニペットでは、Fail-Safe Iteratorを使用しています。 したがって、反復中にコレクションに新しい要素が追加されても、例外はスローされません。

ConcurrentHashMapのデフォルトのイテレータは弱一貫性があります。 つまり、この Iterator は同時変更を許容でき、 Iterator が構築されたときに存在していた要素をトラバースし、 Collection [への変更を反映する可能性があります(ただし、保証されません)。 イテレータの構築後のX228X]。

したがって、上記のコードスニペットでは、反復が5回ループします。つまり、は、コレクションに新しく追加された要素を検出します。

4. 結論

このチュートリアルでは、Fail-SafeおよびFail-Fast Iterators の意味と、これらがJavaでどのように実装されているかを確認しました。

この記事で紹介する完全なコードは、GitHubから入手できます。