wangjie-fourth 的个人博客

may be just need work hard

目录
CAS:Compare And Swap
/        

CAS:Compare And Swap

CAS

CAScompare and swap,是计算机中的一种同步手段。即在更换值得一瞬间,比较当前值是不是想要更换得值,如果是,就更改;反之就不修改。用代码解释:

public class Demo{
    private static int globalI = 0;
    
    public static void main(String[] args){
        new Thread(() -> {
            globalI++;
        }).start();
        
        globalI++;
    }
}

这里主线程和子线程会同时更新globalI,这就会有线程安全问题。下面用CAS解决这个问题:

public class Demo{
    private static int globalI = 0;
    
    public static void main(String[] args){
        new Thread(() -> {
            increment();
        }).start();
        
        increment();
    }
    
    public static void increment(){
        do{
            int oldValue = globalI;
            int newValue = globalI;
        }while(!compareAndSet(oldValue, newValue));
    }
    
    /**
    原子操作:当globalI == oldValue时,才去更新,返回true;否则返回false;
    */
    public static boolean compareAndSet(int oldValue,int newValue){
        
    }
}

当在多线程对共享变量value进行操作的时候,只有当oldValue==现在的值,才会去更新。

ABA问题

CAS在更新值得时候,会涉及到三个概念词。更新前值为old,更新时值为expect,更新后得值为new。只有当:old == expect时,才会将值置为new。否则得话,就不会更新。

但这里存在一个问题:

假设某一个线程准备更新这个值,也就是说这个线程保存old了,此时这个值为**A。然后,另一个线程进来了,它先将值修改成B,然后又将这个值修改成A**。这时候,第一个线程又开始准备修改这个值,它可以成功修改这个值。这个时候,是不符合CAS修改值得条件,此时得A并不是之前得A了。

如果这个值是基本数据类型得话,可能没啥影响。但是涉及到引用变量得话,就会有问题。比如说一个链表:

image.png

可以看到第一个线程可以成功,但它原本是想达到这个效果得:

image.png

如何解决ABA问题

在更新得时候,不仅仅比较值是否相等,还去比较版本号或者时间戳。

即在更新值得时候,记录时间戳或者版本号。下次更新得时候,比对一下时间戳或者版本号。这样通过时间戳和版本号就可以区分当前得A是不是之前得A了。

评论