谈谈CAS实现机制

发布时间:2024年01月19日

CAS伪代码

boolean CAS(address, expectValue, swapValue) {
 if (&address == expectedValue) {
 &address = swapValue;
 return true;
 }
 return false;
}

CAS的全称为 compare and swap,这是一个特殊的cpu指令,完成的是“比较和交换”

我们这里提到的expectValue,swapValue指的都是寄存器中的值

这里的if语句是来去比较address内存地址中的值是否和expected寄存器中的值是否相同,如果相同就会把swap寄存器的值和address中的值进行交换,并且返回true。

如果不相同,则不交换,返回false。

我们所说的这个CAS——CPU指令,他是具有原子性的,也就是说,他是线程安全的,我们可以在多线程来进行使用CAS来确保线程安全。

在CAS指令中,我们不用去进行加锁,也不会阻塞,也能够保证线程安全。

原子类介绍

处于java.util.concurrent.atomic包里

import java.util.concurrent.atomic.AtomicInteger;

public class Demo1 {
        private static AtomicInteger count=new AtomicInteger(0);
        //相当于原先使用
    //private static int count=0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            for (int i = 0; i <50000 ; i++) {
                // count++;
                count.getAndIncrement();
                //++count
                //  count.incrementAndGet();
                //count+=n;
                //count.getAndAdd(n);

            }
        });
        Thread t2=new Thread(()->{
            for(int i=0;i<50000;i++){
                count.getAndIncrement();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("count="+count);
    }
}

运行结果

此处我们可以看到,没有加锁并没有出现错误.这里我们使用了CAS来确保线程安全.

伪代码实现

class AtomicInteger {
 private int value;
 public int getAndIncrement() {
 int oldValue = value;
 while ( CAS(value, oldValue, oldValue+1) != true) {
 oldValue = value;
 }
 return oldValue;
}

这里我们的CAS就是只有value值和oldvalue寄存器的值一样的时候,才能进行交换,返回true,如果不相等,则不会进行交换,并且发回false,给oldValue寄存器重新赋值,再次进行CAS操作,直到CAS操作成功.

谈CAS实现自旋锁

public class SpinLock {
 private Thread owner = null;
 public void lock(){
 // 通过 CAS 看当前锁是否被某个线程持有. 
 // 如果这个锁已经被别的线程持有, 那么就?旋等待. 
 // 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程. 
 while(!CAS(this.owner, null, Thread.currentThread())){
 }
 }
 public void unlock (){
 this.owner = null;
 }
}

当这个锁没有被线程持有的时候,即不等于null,则被当前的线程持有,如果已经持有线程了,则不会被当前线程持有,会一直进行while循环,通过这样的忙碌来等待效果.

此处自旋式的等并没有放弃cpu,因此不会再调度上有开销,但是会浪费更多的cpu资源.

相较于阻塞式的等,完全放弃了cpu,从cpu上离开,不会浪费cpu资源.

CAS的ABA问题,CAS确保线程安全的底层原理在于比较他的值是否有变化,如果进行了变化,则重新对寄存器的值进行赋值,如果没有则完成,但是在这里,如果有一个线程穿插执行,记为线程三,线程一将0改到50,线程2将50改到0,线程三再拿到之前第一个线程的0,再次进行操作,这样看貌似没有什么错误,但实则线程三拿到的0并非是正确的0,

假如在这里有个银行,A余额有1000元,进行取款500,但是点了一次没反应,再点了一次,

为了避免这样的情况

我们有两种解决方案:

1)我们对数据进行约束,规定数据的变化只能是单向的(只能增加或者减少),不能是双向的,(既增加又减少).

2)对于本身就必须双向变化的数据,我们引入一个版本号,这个版本号数字只能增加不能减少.

文章来源:https://blog.csdn.net/m0_73700505/article/details/135684795
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。