『マイクロサービス実戦』第27章 CAS

シリーズ記事ディレクトリ

第 28 章 分散ロック フレームワーク - Redisson
第 27 章 CAS
第 26 章 Java ロックの分類
第 25 章 Java マルチスレッド セキュリティとロック
第 2 章 CountDownLatch と Semaphone の応用
第 1 章 Java スレッド プール テクノロジの応用

ここに画像の説明を挿入



序文

この章では、CAS の概念と原理を紹介し、Java コードを介して CAS を実装します。

1. CASの概念

CAS の正式名は CompareAndSwap で、文字通りには「比較と交換」と訳されます。
CAS は一般的なプロセッサでサポートされている命令で、変数のアトミック性を実現するために、現在のメモリ値 V と古い期待値 A と更新される値 B が等しいかどうかを比較して新しい値を設定する命令です。
同期されたスレッドのブロックは悲観的ロックと呼ばれますが、CAS では楽観的ロックと呼ばれるスレッドのブロックは行われません。悲観的ロックやロックを取得していないスレッドはコードを実行しませんが、楽観的ロックでは複数のスレッドが同時にコードにアクセスできますが、更新があるかどうかを判断して再実行するかどうかを決定します。

2. CASの実現原理

CAS の原理: 現在のメモリの変数値 V、古い期待値 A、および更新される値 B の 3 つのパラメータを通じて。V と A が等しいかどうかで、現在の変数の値が他のスレッドによって変更されているかどうかを確認し、等しい場合には V に B の値を代入し、等しくない場合にはスピン操作を実行します。
例: i の初期値が 0 で、2 つのスレッドがそれぞれ i++ 操作を実行すると仮定し、CAS がどのように実行されるかを確認します。

1 スレッド: A=0、B=1
2 スレッド: A=0、B=1

このとき、両スレッドの古い期待値Aと更新後のB値は同じであり、
スレッド1が先に実行され、スレッド1がiの値Vをメモリから取得したとする。
2 スレッドが実行されると、この時に得られるメモリの V 値は 1 であり、古い期待値 0 は待ちたくないため、B を更新する代入操作は行われず、スピンによって古い期待値 A=V が決定され、CAS 命令が再度決定されます。
ここに画像の説明を挿入

JDK によって提供されるアトミック操作クラスは、AtomicInteger、AtomicIntegerArray、AtomicDouble、AtomicBoolean など、アトミック性を実現する CAS に基づいています。

3. 単一の JVM 内部ロック CAS 実装

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/***
 * @title AtomicExample
 * @desctption CAS
 * @author Kelvin
 * @create 2023/6/14 17:08
 **/
public class AtomicExample {
    
    
    private static Map<String, Boolean> map = new HashMap<>();

    public static void main(String[] args) throws InterruptedException {
    
    
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //未加锁
        map.put("lock", true);
        for (int i = 0; i < 20; i++) {
    
    
            int finalI = i;
            executorService.submit(
                    () -> {
    
    
                        boolean isRotate = true;
                        while (isRotate) {
    
    
                            boolean vLock = map.get("lock");
                            if( vLock ) {
    
    
                                boolean aLock = map.get("lock");
                                if( vLock == aLock ) {
    
    
                                    //执行业务逻辑
                                    //加锁
                                    map.put("lock", false);
                                    System.out.println( "执行业务逻辑, i: " + finalI);
                                    try {
    
    
                                        Thread.sleep(2000);
                                    } catch (InterruptedException e) {
    
    
                                        throw new RuntimeException(e);
                                    }
                                    isRotate = false;
                                    //释放锁
                                    map.put("lock", true);
                                } else {
    
    
                                    System.out.println("自旋,重新获取锁!");
                                    continue;
                                }
                            }
                        }
                    }
            );
        }

        Thread.sleep(20 * 1000);
        System.out.println("end");
        executorService.shutdown();
        
    }
}

3.1. 効果

ビジネス ロジックを実行、i: 1
ビジネス ロジックを実行、i: 5
スピン、ロックを再取得します。
スピンしてロックを再取得してください!
スピンしてロックを再取得してください!
スピンしてロックを再取得してください!
スピンしてロックを再取得してください!
スピンしてロックを再取得してください!
ビジネス ロジックを実行します。i: 4
スピン、ロックを再取得します。
スピンしてロックを再取得してください!
ビジネス ロジックを実行します。i: 10
スピン、ロックを再取得します。
スピンしてロックを再取得してください!
ビジネス ロジックを実行、i: 9
ビジネス ロジックを実行、i: 2
スピン、ロックを再取得します。
ビジネス ロジックを実行します。i: 3
スピン、ロックを再取得します。
ビジネス ロジックを実行、i: 0
ビジネス ロジックを実行、i: 12
ビジネス ロジックを実行、i: 11
ビジネス ロジックを実行、
i: 8
ビジネス ロジックを実行、i: 6 ビジネス ロジックを実行、i: 7
ビジネス ロジックを実行、i: 14
スピン、ロックを再取得します。
ビジネス ロジックを実行、i: 15
ビジネス ロジックを実行、i: 16
ビジネス ロジックを実行、i: 18
スピン、ロックを再取得します。
スピンしてロックを再取得してください!
ビジネス ロジックの実行、i: 17
ビジネス ロジックの実行、i: 19
ビジネスロジックを実行、i:13
終了

おすすめ

転載: blog.csdn.net/s445320/article/details/130951405