ケースを解決するために、JavaのCAS原理

マルチスレッド環境での1、シングルトン潜在的なセキュリティ問題

package com.example.mybaties;

/**
 * @DESCRIPTION 单例模式在多线程环境下可能存在安全问题
 * @Author lst
 * @Date 2020-03-24 09:00
 */
public class SingletonDemo {
    //高并发下加载完成再取值  volatile(禁止指令重排),不加volatile,99.9%是成功的
    private static volatile SingletonDemo instance = null;

    public SingletonDemo(){
        System.out.println(Thread.currentThread().getName()+"\t  我是构造方法SingletonDemo() ");
    }

    /**
     * 可以加synchronized(效率低,太重了)
     * @return
     */
   /* public static synchronized SingletonDemo getInstance(){
        if(instance == null){
            instance = new SingletonDemo();
        }
        return instance;
    }*/

    /**
     * 高并发下   DCL双端检索机制
     */
    public static synchronized SingletonDemo getInstance(){
        if(instance == null){
            //同步代码块  加锁前后判断
            synchronized (SingletonDemo.class){
                if(instance == null){
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        //单线程
       /* System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());*/
        /**
         * main	  我是构造方法SingletonDemo()
         * true
         * true
         * true
         */


        //并发多线程后,情况发生了很大的变化
        for (int i = 1; i <= 10; i++) {
            new Thread(() ->{
                SingletonDemo.getInstance();
            },String.valueOf(i)).start();
        }
        /**
         * 1	  我是构造方法SingletonDemo()
         * 5	  我是构造方法SingletonDemo()
         * 3	  我是构造方法SingletonDemo()
         * 2	  我是构造方法SingletonDemo()
         */
    }

}

2、CASの実装原理

比較およびスワップ(Conmpareとスワップ)マルチスレッドの同期を実現するためのアトミック命令です。JAVA1.5がCASの導入を開始し、メインコードは、原子JUCパッケージの下に配置されています

CAS(楽観的ロック)を効果的に並行処理の効率を向上させるだけでなく、ABAの問題を導入することができます。

 メモリ・X、A、別のスレッドA 2は、メモリXから除去され、このとき、スレッド2から除去スレッド1がターン・スレッド2内のメモリにB、およびXのメモリXに値の一部を操作しますデータとなり、スレッド1 CAS操作がまだメモリXで発見され、この時間は、1つの操作が成功したスレッドです。CASの成功は、1つの操作をスレッドが、全体のプロセスには問題があるが。例えば、リストの先頭の変化は、二人は元の値に戻った後、しかし、リストは変更はありませんという意味ではありません。

 JAVAは、問題が発生して、対処する主オブジェクトにターゲットを変更したかどうかを識別するために、追加のタグを追加するAtomicStampedReference / AtomicMarkableReferenceのABAのシーンを提供しますので。

package com.example.mybaties;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @DESCRIPTION CAS
 *   1、cas是什么?compareAndSet(期望值,修改值)
 *       比较并交换
 *       unsafe和自旋锁
 *      ①、unsafe是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地( native)方法来访问, Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据,
 *          Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中CAS操作的执行依赖于 Unsafe类的方法。
 *       注意 Unsafe类中的所自方法都是 native修饰的,也就是说 Unsafe类中的方法都直接调用操作系统底层资源执行相应任务
 *      ②、自旋锁代码原理
 *           public final int getAndAddInt(Object var1, long var2, int var4) {
 *                 int var5;
 *                 do {
 *                     var5 = this.getIntVolatile(var1, var2);
 *                  } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
 *                return var5;
 *          }
 *      CAS缺点:① 如果CAS失敗,会一直进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销
 *               ② 只能保证一个共享变量的原子操作
 *               ③ 引出ABA问题(狸猫换太子)
 *      CAS--->Unsafe ---》cas底层思想 ---》ABA ---》原子引用更新 ---》如何规避ABA问题
 * @Author lst
 * @Date 2020-03-24 09:00
 */
public class CasDemo {


    public static void main(String[] args) {
        //主物理内存是5
        AtomicInteger atomicInteger = new AtomicInteger(5);

        //main do thing
        System.out.println(atomicInteger.compareAndSet(5,2020) + "\t current data:"+ atomicInteger.get());
        /**
         * true	 current data:2020
         */
        System.out.println(atomicInteger.compareAndSet(5,1024) + "\t current data:"+ atomicInteger.get());
        /**
         * true	     current data:2020
         * false	 current data:2020
         */
        //atomicInteger.getAndIncrement();
    }
}

ABAの問題を解決するために3、

package com.example.mybaties;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @DESCRIPTION ABA问题的解决
 * @Author lst
 * @Date 2020-03-26 09:00
 */
public class AbaDemo {  //AtomicStampedReference

    static AtomicReference atomicReference = new AtomicReference(100);


    static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(100,1);

    public static void main(String[] args) {
        new Thread(()->{
            atomicReference.compareAndSet(100,101);
            atomicReference.compareAndSet(101,100);
        },"t1").start();

        new Thread(()->{
            //暂停一会线程
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100,2020) + "\t "+ atomicReference.get());
            /**
             * true	 2020
             */
        },"t2").start();

        //暂停一会线程
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("====================以下是ABA问题的解决==================");

        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t 第一次版本:" + stamp);
            //暂停一会线程
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName() + "\t 第二次版本:" + atomicStampedReference.getStamp());

            atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName() + "\t 第三次版本:" + atomicStampedReference.getStamp());
        },"t3").start();


        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t 第一次版本:" + stamp);
            //暂停一会线程 保证上面的t3线程完成ABA操作
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean result = atomicStampedReference.compareAndSet(100,2020,stamp,stamp+1);
            System.out.println(Thread.currentThread().getName() + "\t 修改成功否" + result + "\t 当前最新实际版本号:" + atomicStampedReference.getStamp());
            System.out.println(Thread.currentThread().getName() + "\t 当前实际最新值:" + atomicStampedReference.getReference());
        },"t4").start();

        /**
         * true	 2020
         * ====================以下是ABA问题的解决==================
         * t3	 第一次版本:1
         * t4	 第一次版本:1
         * t3	 第二次版本:2
         * t3	 第三次版本:3
         * t4	 修改成功否false	 当前最新实际版本号:3
         * t4	 当前实际最新值:100
         */
    }
}

 

 

公開された23元の記事 ウォン称賛33 ビュー30000 +

おすすめ

転載: blog.csdn.net/qq_33612228/article/details/105202545