理解理解CAS和Volatile

CAS的理解

什么是CAS

CompareAndSweep比较并交换。

AtomicInteger atomicInteger = new AtomicInteger(1);
//期望值是1,如果达到,那么就更新为2,否则不更新
atomicInteger.compareAndSet(1,2);

自旋锁

在这里插入图片描述
比较当前线程工作内存中的值和主内存中的值,如果这个值是期望的,那么就执行操作,否则一直循环
缺点:

  • 循环会耗时。
  • 一次性只能保证一个共享变量的原子性。
  • 存在ABA问题。

ABA问题

前面可能出现ABA问题,什么是ABA问题?
简单来说,就是当线程A在操作某个值时,线程B将主存中的值改为某值后又还原,线程A不知道这个值已经被人修改过。

如何解决?

通过原子引用
也就是一个带版本号的类,类似于数据库的乐观锁。

//这里设置值的时候传一个版本号
AtomicStampedReference<Integer> asr= new AtomicStampedReference<>(1,1);
//这里操作这个值的时候就将版本号加1,返回值为true则说明这个值未被别的线程修改过,false反之。
asr.compareAndSet(1,2, asr.getStamp(),asr.getStamp()+1);

Volatile

Volatile是java虚拟机提供的轻量级的同步机制
1.保证可见性
2.不保证原子性
3.禁止指令重排

可见性体会

public class JMMDemo {
    
    
    //保证可见性,如果不加,线程1会死循环
    private volatile static int num = 0;

    public static void main(String[] args) {
    
    
        new Thread(()->{
    
     //线程1
            int count = 0;
            while(num == 0){
    
    
            }
        },"A").start();

        try {
    
    
            TimeUnit.SECONDS.sleep(1);
        } catch(Exception e){
    
    
            e.printStackTrace();
        }
        num = 1;
        System.out.println(Thread.currentThread().getName()+num);
    }
}

不保证原子性体会

//不保证原子性
public class VDemo02 {
    
    
    private volatile static int num = 0;
    public static void add(){
    
    
        num++;//不是一个原子性操作
    }
    public static void main(String[] args) {
    
    
        //理论num结果应该为2万
        for (int i = 1; i <= 20; i++) {
    
    
              new Thread(() ->{
    
    
                  for(int j = 0; j < 1000; j++){
    
    
                      add();
                  }
              },String.valueOf(i)).start();
        }
        while(Thread.activeCount() > 2){
    
    
            Thread.yield();
        }
		System.out.println(Thread.currentThread().getName() + " " + num);
    }
}
  • 如上操作,num++不是一个原子操作,多个线程共同操作这个方式,会导致num的值不为预期值20000
  • 要解决这个问题,可以使用原子类,也就是上面使用到的AtomicInteger来修饰num。

禁止指令重排

我们写的程序,计算机并不是按照我们写的那样执行。
源代码 --> 编译器优化的重排–>指令并行也可能会重排–>内存系统也会重排–>执行
处理器进行指令重排,会考虑数据之间的依赖性。

而被Volatile修饰的变量在写操作时,会生成内存屏障,保证操作的执行顺序,指令不会被重排。

猜你喜欢

转载自blog.csdn.net/qq_41570752/article/details/108521900