ホームページ Java &#&チュートリアル フェイルファストメカニズム

フェイルファストメカニズム

Sep 02, 2019 pm 04:39 PM
java

JDK コレクションでは、次のような単語がよく見られます:

フェイルファストメカニズム

たとえば、ArrayList:

注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽
最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭
代器的快速失败行为应该仅用于检测 bug。
ログイン後にコピー

HashMap:

注意,迭代器的快速失败行为不能得到保证,一般来说,存在非同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大
努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应
该仅用于检测程序错误。
ログイン後にコピー

これら 2 つの段落で「フェイルファスト」について繰り返し言及しています。では、「フェイルファスト」メカニズムとは何でしょうか?

「高速障害」はフェイルファストであり、Java コレクションのエラー検出メカニズムです。複数のスレッドがコレクションの構造変更を実行すると、フェイルファスト メカニズムが発生する可能性があります。可能性はあるが、確実ではないことを覚えておいてください。例: 2 つのスレッドがあるとします (スレッド 1、スレッド 2)。スレッド 1 は反復子を介してセット A の要素をトラバースしています。ある時点で、スレッド 2 はセット A の構造を変更します (これは構造の変更であり、変更ではありません)。コレクション要素の内容を単純に変更する場合)、プログラムはこの時点で ConcurrentModificationException 例外をスローし、フェイルファスト メカニズムが発生します。

#1. フェイルファストの例

public class FailFastTest {
    private static List<Integer> list = new ArrayList<>();
 
    /**
     * @desc:线程one迭代list
     * @Project:test
     * @file:FailFastTest.java
     * @Authro:chenssy
     * @data:2014年7月26日
     */
    private static class threadOne extends Thread{
        public void run() {
            Iterator<Integer> iterator = list.iterator();
            while(iterator.hasNext()){
                int i = iterator.next();
                System.out.println("ThreadOne 遍历:" + i);
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
 
    /**
     * @desc:当i == 3时,修改list
     * @Project:test
     * @file:FailFastTest.java
     * @Authro:chenssy
     * @data:2014年7月26日
     */
    private static class threadTwo extends Thread{
        public void run(){
            int i = 0 ;
            while(i < 6){
                System.out.println("ThreadTwo run:" + i);
                if(i == 3){
                    list.remove(i);
                }
                i++;
            }
        }
    }
 
    public static void main(String[] args) {
        for(int i = 0 ; i < 10;i++){
            list.add(i);
        }
        new threadOne().start();
        new threadTwo().start();
    }
}
ログイン後にコピー

実行結果:

ThreadOne 遍历:0
ThreadTwo run:0
ThreadTwo run:1
ThreadTwo run:2
ThreadTwo run:3
ThreadTwo run:4
ThreadTwo run:5
Exception in thread "Thread-0" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    at java.util.ArrayList$Itr.next(Unknown Source)
    at test.ArrayListTest$threadOne.run(ArrayListTest.java:23
ログイン後にコピー

2. フェイルファストの原因

上記の例と説明を通じて、フェイルファストの理由は、プログラムがコレクションを反復するときに、スレッドがコレクションに構造的な変更を加えるためであることが最初にわかりました。このとき、反復子の ConcurrentModificationException 例外情報は、スローされ、フェイルファストが発生します。

フェイルファスト メカニズムを理解するには、まず ConcurrentModificationException 例外を理解する必要があります。この例外は、メソッドがオブジェクトの同時変更を検出したが、そのような変更は許可しない場合にスローされます。同時に、この例外は、オブジェクトが異なるスレッドによって同時に変更されたことを常に示すわけではないことに注意してください。単一のスレッドがルールに違反した場合も、例外がスローされる可能性があります。

確かにイテレータのフェイルファスト動作は保証できず、このエラーが発生することも保証されませんが、フェイルファスト操作は ConcurrentModificationException 例外をスローするために最善を尽くします。このような操作の精度を向上させるために、この例外に依存するプログラムを作成するのは間違いであり、正しいアプローチは次のとおりです: ConcurrentModificationException はバグを検出するためにのみ使用する必要があります。以下では、ArrayList を例として使用して、フェイルファストの理由をさらに分析します。

イテレータを操作するときにフェイルファストが発生することは前からわかっています。次に、ArrayList のイテレータのソース コードを見てみましょう。

private class Itr implements Iterator<E> {
        int cursor;
        int lastRet = -1;
        int expectedModCount = ArrayList.this.modCount;
 
        public boolean hasNext() {
            return (this.cursor != ArrayList.this.size);
        }
 
        public E next() {
            checkForComodification();
            /** 省略此处代码 */
        }
 
        public void remove() {
            if (this.lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
            /** 省略此处代码 */
        }
 
        final void checkForComodification() {
            if (ArrayList.this.modCount == this.expectedModCount)
                return;
            throw new ConcurrentModificationException();
        }
    }
ログイン後にコピー

上記のソース コードから、next() および Remove() を呼び出すときにイテレータが常に checkForComodification() メソッドを呼び出すことがわかります。このメソッドは主に modCount == ExpectedModCount? を検出するためのもので、そうでない場合は ConcurrentModificationException 例外がスローされ、フェイルファスト メカニズムが生成されます。したがって、フェイルファスト メカニズムが発生する理由を理解するには、modCount != ExpectedModCount の理由と、それらの値がいつ変更されたかを理解する必要があります。

expectedModCount は Itr で定義されています: int ExpectedModCount = ArrayList.this.modCount; したがって、その値は変更できないため、変更されるのは modCount です。 modCount は AbstractList で定義され、グローバル変数です:

 protected transient int modCount = 0;
ログイン後にコピー

それでは、いつ、どのような理由で変更されるのでしょうか? ArrayList のソース コードを見てください:

public boolean add(E paramE) {
    ensureCapacityInternal(this.size + 1);
    /** 省略此处代码 */
}

private void ensureCapacityInternal(int paramInt) {
    if (this.elementData == EMPTY_ELEMENTDATA)
        paramInt = Math.max(10, paramInt);
    ensureExplicitCapacity(paramInt);
}

private void ensureExplicitCapacity(int paramInt) {
    this.modCount += 1;    //修改modCount
    /** 省略此处代码 */
}

public boolean remove(Object paramObject) {
    int i;
    if (paramObject == null)
        for (i = 0; i < this.size; ++i) {
            if (this.elementData[i] != null)
                continue;
            fastRemove(i);
            return true;
        }
    else
        for (i = 0; i < this.size; ++i) {
            if (!(paramObject.equals(this.elementData[i])))
                continue;
            fastRemove(i);
            return true;
        }
    return false;
}

private void fastRemove(int paramInt) {
    this.modCount += 1;   //修改modCount
    /** 省略此处代码 */
}

public void clear() {
    this.modCount += 1;    //修改modCount
    /** 省略此处代码 */
}
ログイン後にコピー
上記のソース コードから、ArrayList 要素の数の変更が含まれる限り、ArrayList の add、remove、clear メソッドに関係なく、 , modCountが変化します。したがって、ここでは、expectedModCount 値と modCount の変化が同期していないため、両者の間に不等号が生じ、フェイルファスト メカニズムが発生すると事前に判断できます。フェイルファストの根本原因がわかれば、次のようなシナリオが考えられます。

2 つのスレッド (スレッド A、スレッド B) があり、そのうちのスレッド A はリストの走査を担当し、スレッド B はリストを変更します。 。スレッド A がリストを走査しているとき (この時点では ExpectModCount = modCount=N)、スレッドが開始され、同時にスレッド B が要素を追加します。これは、modCount の値が変化することを意味します (modCount 1 = N 1)。 。スレッド A が引き続きトラバースして次のメソッドを実行すると、checkForComodification メソッドに、expectedModCount = N と modCount = N 1 が等しくないことが通知され、このとき、ConcurrentModificationException 例外がスローされ、失敗します。速いメカニズム。

ということで、ここまででフェイルファストの根本原因を完全に理解しました。原因がわかれば、解決策も見つけやすくなります。

3. フェイルファスト ソリューション

これまでの例とソース コード分析を通じて、フェイルファストのメカニズムを基本的に理解できたと思います。理由と解決策。ここには 2 つの解決策があります。

解決策 1: トラバーサル プロセス中に、modCount 値の変更を伴うすべての場所に synchronized を追加するか、Collections.synchronizedList を直接使用すると、問題を解決できます。ただし、追加や削除によって発生する同期ロックによってトラバーサル操作がブロックされる可能性があるため、これはお勧めできません。

オプション 2: CopyOnWriteArrayList を使用して ArrayList を置き換えます。この解決策をお勧めします。

以上がフェイルファストメカニズムの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Javaの平方根 Javaの平方根 Aug 30, 2024 pm 04:26 PM

Java の平方根のガイド。ここでは、Java で平方根がどのように機能するかを、例とそのコード実装をそれぞれ示して説明します。

Javaの完全数 Javaの完全数 Aug 30, 2024 pm 04:28 PM

Java における完全数のガイド。ここでは、定義、Java で完全数を確認する方法、コード実装の例について説明します。

Java の乱数ジェネレーター Java の乱数ジェネレーター Aug 30, 2024 pm 04:27 PM

Java の乱数ジェネレーターのガイド。ここでは、Java の関数について例を挙げて説明し、2 つの異なるジェネレーターについて例を挙げて説明します。

ジャワのウェカ ジャワのウェカ Aug 30, 2024 pm 04:28 PM

Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

Javaのスミス番号 Javaのスミス番号 Aug 30, 2024 pm 04:28 PM

Java のスミス番号のガイド。ここでは定義、Java でスミス番号を確認する方法について説明します。コード実装の例。

Java Springのインタビューの質問 Java Springのインタビューの質問 Aug 30, 2024 pm 04:29 PM

この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

Java 8 Stream Foreachから休憩または戻ってきますか? Java 8 Stream Foreachから休憩または戻ってきますか? Feb 07, 2025 pm 12:09 PM

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

Java での日付までのタイムスタンプ Java での日付までのタイムスタンプ Aug 30, 2024 pm 04:28 PM

Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

See all articles