CAS principle of java to solve the case

1, singleton potential security problem in a multithreaded environment

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 implementation principle

Compare and Swap (Conmpare And Swap) is an atomic instruction for realizing multi-thread synchronization. JAVA1.5 began the introduction of CAS, the main code are placed under the atomic JUC package

CAS (optimistic locking) can effectively improve the efficiency of concurrency, but also introduce the ABA problem.

 The thread 1 removed from memory X, A, this time another thread A 2 is also removed from memory X, and the thread 2 operate some of the values ​​into memory X to B, and X in the memory in turn thread 2 the data becomes a, this time thread 1 CAS operation is still found in the memory X is a, then thread 1 operation was successful. Although the success of CAS thread 1 operations, but the whole process is problematic. For example, a change in the head of the list after the two returned to the original value, but the list does not mean that there will be no change.

 So JAVA provides AtomicStampedReference / AtomicMarkableReference ABA scene to deal with the problem occurs, mainly in objects to add additional tags to identify whether a target had changed.

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();
    }
}

3, to solve the problem of ABA

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
         */
    }
}

 

 

Published 23 original articles · won praise 33 · views 30000 +

Guess you like

Origin blog.csdn.net/qq_33612228/article/details/105202545