Javaマルチスレッドのメモリ可視性の問題を解決する2つの方法:同期、揮発性、およびCASアルゴリズム


序文

まず、Javaのメモリモデルを簡単に理解してから、コードの一部に基づいてメモリの可視性の問題を引き起こします。

  • Javaメモリモデル
    Javaメモリモデルでは、すべての変数がメインメモリに格納されることが規定されています。各スレッドには独自の作業メモリがあります。スレッドの作業メモリには、スレッドが使用する変数が格納されます(これらの変数はコピー元のメインメモリからのものです) 。スレッドによる変数のすべての操作(読み取り、割り当て)は、作業メモリーで実行する必要があります。異なるスレッドが互いの作業メモリ内の変数に直接アクセスすることはできず、スレッド間の変数値の転送はメインメモリを介して完了する必要があります

  • コード例

public class 内存可见性问题 {
    
    
    public static void main(String[] args) {
    
    
        MyRunable0 myRunable0 = new MyRunable0();
        Thread th = new Thread(myRunable0);
        th.start();
        while (true){
    
    
            //程序没有进入,线程在其工作内存中将共享变量值修改为true,但是没能及时刷新到主存中
            if(myRunable0.isFlag()){
    
    
                System.out.println("进来了");
                break;
            }
        }

    }
}
class MyRunable0 implements Runnable{
    
    
    boolean flag=false;

    public boolean isFlag() {
    
    
        return flag;
    }

    public void setFlag(boolean flag) {
    
    
        this.flag = flag;
    }

    @Override
    public void run() {
    
    
        flag=true;
        System.out.println("线程将flag改成"+flag);

    }
}
  • 運転結果
    ここに画像の説明を挿入
  • 可視性
    スレッドによる共有変数値の変更は、他のスレッドによって時間内に確認できます。
  • Atomicity
    Atomicityは、操作が分割できないことを意味します。マルチコアであろうとシングルコアであろうと、原子量であっても、一度に1つのスレッドしか操作できません。
  • 目に見える状態を達成する
  1. スレッドによって変更された共有変数値は、作業メモリーからメインメモリーに時間内に更新できます。
  2. 他のスレッドは、共有変数の最新の値をメインメモリから独自の作業メモリに時間内に更新できます
  • Java言語レベルでサポートされる可視性の実装
  1. 同期
  2. 揮発性

1つは、可視性を実現するために同期されます

1.同期を実現できます

  • アトミシティ(同期)
  • 可視性

2.同期に関するJVMの2つの規制

  • スレッドのロックを解除する前に、共有変数の最新の値をメインメモリにフラッシュする必要があります。
  • スレッドがロックされると、作業メモリー内の共有変数の値がクリアされるため、共有変数を使用する場合は、メインメモリーから最新の値を再度読み取る必要があります。

3.コード

        while (true){
    
    
        //使用 synchronized实现可见性,注意锁的使用
           synchronized (myRunable0){
    
    
               if (myRunable0.isFlag()) {
    
    
                   System.out.println("进来了");
                   break;
               }
           }
        }

4.同期のデメリット

  • ブロックの実際の長さを制御できません
  • ブロッキングを中断することはできません
  • 低効率

2、揮発性は可視性を実現します

1.揮発性を達成することができます

  • 可視性の問題
  • 原子性の保証はありません

2.実装の原則

これは、メモリバリアを追加し、並べ替えの最適化を禁止することで実現されます。

  • 揮発性変数への書き込み操作を実行する場合、書き込み操作の後にストアバリア命令が追加されます
  • 揮発性変数に対して読み取り操作を実行する場合、読み取り操作の前にロードバリア命令が追加されます

素人の言葉で言えば、スレッドが揮発性変数にアクセスするたびに、変数の値がメインメモリから強制的に再読み取りされます。変数が変更されると、スレッドは最新の値をメインに更新します。メモリ。このようにして、いつでも、異なるスレッドが変数の最新の値を常に見ることができます。

3.コード

		//在共享变量前添加volatile关键字
 volatile boolean flag=false;

4.揮発性の適用可能なシナリオ

  • 変数への書き込み操作は、現在の値に依存しません
  • 変数は他の変数との不変量に​​含まれていません

3. CAS(Compare-And-Swep)アルゴリズム

1。概要

  • CAS、コンペアアンドスワップの比較と交換。合計3つのオペランド、メモリ値v、スレッドローカルメモリの古い値a(目的の操作の前の値)、および新しい値bがあります。操作中に、古い値aとメモリ値vを比較して変更を確認します。 。変更がない場合、メモリ値vを新しい値bに更新できます。変更がある場合、それは交換されません。

2.コード

public class CAS算法 {
    
    
    public static void main(String[] args) {
    
    
        Mrunable mrunable = new Mrunable();
        for (int i = 0; i < 10; i++) {
    
    
            new Thread(mrunable).start();
        }

    }
}
class Mrunable implements Runnable{
    
    
    //Java提供好的原子变量
    AtomicInteger i=new AtomicInteger(1);
    
    public AtomicInteger getI() {
    
    
        return i;
    }

    @Override
    public void run() {
    
    
        while (true){
    
    
            try {
    
    
                Thread.sleep(50);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            //使用原子变量调用相应的方法完成需求
            System.out.println(i.getAndIncrement());
        }


    }
}

総括する

同期化された揮発性の比較

  • Volatileはロックを必要とせず、同期よりも軽量で、スレッドをブロックしません
  • メモリの可視性の観点から、揮発性の読み取り操作はロックと同等であり、揮発性の書き込み操作はロック解除と同等です。
  • Synchronizedは可視性と原子性の両方を保証できますが、volatileは可視性のみを保証でき、原子性は保証できません。

おすすめ

転載: blog.csdn.net/m0_46988935/article/details/112937371