ホームページ  >  記事  >  Java  >  マルチスレッド環境での Java 命令の並べ替えを解決する方法

マルチスレッド環境での Java 命令の並べ替えを解決する方法

PHPz
PHPz転載
2023-04-19 15:40:061261ブラウズ

1. はじめに

命令の再配置は、シングルスレッド環境ではプログラムの実行効率の向上に役立ち、プログラムに悪影響を及ぼしませんが、マルチスレッド環境では、命令の再配置は、プログラムに驚きをもたらす、予期しないエラー。

2. 問題の回復

(1) 関連する変数

以下は命令の並び替えを完全に回復できる例です。

public class D {
    static Integer a;
    static Boolean flag;
    
    public static void writer() {
        a = 1;
        flag = true;
    }
    
    public static void reader() {
        if (flag != null && flag) {
            System.out.println(a);
            a = 0;
            flag = false;
        }
    }
}
1. 結果予測

reader メソッドは、flag 変数が true の場合にのみ、変数 a をコンソールに出力します。価値。

writer メソッドは、まず変数 a の代入演算を実行し、次に変数 flag の代入演算を実行します。

上記の分析ロジックに従う場合、コンソールに出力される結果はすべて 1 である必要があります。

2. 命令の再配置

コードに命令の再配置がない場合、flag 変数が true の場合、変数 a は 1 でなければなりません。 。

上記のコードでは、変数 a と変数 flag に関する両方のメソッド クラスで命令の再配置があります。

public static void writer() {
    a = 1;
    flag = true;
}

ログ出力を観察すると、多数の 0 出力があることがわかりました。

writer メソッド内で命令の再配置が発生すると、flag 変数が最初に割り当てを完了します。このとき、現在のスレッドが中断されると、他のスレッドも中断されます。 reader メソッドを呼び出し、flag 変数が true であることを検出し、変数 a の値を出力します。この時点で、コンソールには期待値を超える結果が表示されます。

(2) New でオブジェクトを作成します

new キーワードを使用してオブジェクトを作成する場合、非アトミックな操作であるため、命令の再配置が発生します。マルチスレッド環境への影響。

public class Singleton {
    private static UserModel instance;
    
    public static UserModel getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new UserModel(2, "B");
                }
            }
        }
        return instance;
    }
}

@Data
@AllArgsConstructor
class UserModel {
    private Integer userId;
    private String userName;
}
1. 解析と作成のプロセス
  • キーワード new を使ってオブジェクトを作成するのですが、大まかに次のようなプロセスに分かれます。

    ## スタック スペースに参照アドレスを作成します。
  • #クラス ファイルをテンプレートとして使用して、ヒープ スペース オブジェクトにメモリを割り当てます。

  • #メンバー変数の初期化

  • #コンストラクターを使用して初期化
  • ##参照値を左側の記憶変数に代入
  • #2. 並べ替えプロセスの分析

  • 上記の例では、最初のスレッドが同期されたコード ブロックに入り、オブジェクトの作成を開始すると仮定します。作成プロセスが中断され、スタック領域に参照アドレスが作成された後、参照値が左側の記憶変数に割り当てられ、その後 CPU スケジューリング タイム スライスの枯渇により割り込みが発生したように見える場合があります。
  • 後続のスレッドで

    instance
  • 変数が空ではないことが検出されると、その変数が直接使用されます。シングルトン オブジェクトはインスタンス化されないため、これらを直接使用すると予期しない結果が生じます。
3. 命令の並び替えへの対応

(1) AtomicReference アトミック クラス

アトミック クラスを使用して、その特性を利用して関連する変数のセットをオブジェクトにカプセル化します。アトミックな操作を実現し、コマンドの再配置の問題を効果的に回避します。 <pre class="brush:java;">@Data @NoArgsConstructor @AllArgsConstructor public class ValueModel { private Integer value; private Boolean flag; }</pre>アトミック クラスは、マルチスレッド環境で命令を並べ替える場合に推奨されるソリューションです。理解しやすいだけでなく、スレッド間で使用される非ヘビーウェイト ミューテックスは比較的効率的です。

public class E {
    private static final AtomicReference<ValueModel> ar = new AtomicReference<>(new ValueModel());
    
    public static void writer() {
        ar.set(new ValueModel(1, true));
    }
    
    public static void reader() {
        ValueModel valueModel = ar.get();
        if (valueModel.getFlag() != null && valueModel.getFlag()) {
            System.out.println(valueModel.getValue());
            ar.set(new ValueModel(0, false));
        }
    }
}

関連する変数のグループが命令の再配置を受ける場合、アトミック操作クラスを使用する方が良い解決策です。

(2) volatile キーワード

public class Singleton {
    private volatile static UserModel instance;
    
    public static UserModel getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new UserModel(2, "B");
                }
            }
        }
        return instance;
    }
}

@Data
@AllArgsConstructor
class UserModel {
    private Integer userId;
    private String userName;
}

4. 命令の再配置についての理解

1. 命令の再配置が広く普及している

命令の再配置 配置は整っていないJava プログラムに限定されますが、実際には、ソフトウェアから CPU ハードウェアに至るまで、さまざまなコンパイラで命令の再配置操作が行われます。命令の再配置は、シングル スレッド プログラムのパフォーマンスの最適化です。命令の再配置によって、シングル スレッド環境での順次プログラムの実行で期待される結果が変わるわけではないことは明らかです。

2. マルチスレッド環境での命令の再配置

上記では、2 つの典型的なマルチスレッド環境での命令の再配置について説明し、その悪影響を分析し、それぞれ対策を示しました。

関連する変数の場合は、まずそれらをオブジェクトにカプセル化してから、アトミック クラスを使用して操作します。

新しいオブジェクトの場合は、volatile キーワードを使用しますオブジェクトは、ターゲットを変更できます。

  • 3. 同期ロックは、並べ替えとは何の関係もありません。

  • 同期ロックにより、スレッドが相互接続を通じて順序どおりに特定のコード ブロックにアクセスできるようになります。除外ロック。コード ブロック内のコードは、通常、コンパイラによって実装された戦略に従って並べ替えられます。
  • 同期ロックを使用すると、マルチスレッド環境での並べ替えによる悪影響を回避できますが、ミューテックス ロックによって生じるスレッドのオーバーヘッドが比較的大きいため、お勧めできません。

同期ブロックでの非アトミック操作でも命令の再配置が発生する可能性があります

以上がマルチスレッド環境での Java 命令の並べ替えを解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。