多线程JUC 第2季 CAS的作用介绍与自旋锁

一  CAS作用介绍

1.1 CAS的概念

CAS的全程compare-and-swap,它是一条cpu并发原语,它的功能是判断内存某个位置是否为预期值,如果是则更改为新的值,这个过程是原子的

cas是jdk提供的非阻塞原子操作,它通过硬件保证比较-更新的原子性。它是一条cpu的原子指令,不会造成所谓的数据不一致问题。cas的原子性实际上是cpu实现的独占,比起synchronized重量级锁,这里的排它要更短,所以多线程情况下性能就会比较好。

AtomicInteger类主要利用CAS+voltile和native方法来保证原子操作,从而避免synchronized的高开销,执行效率大为提升。

1.2 CAS作用

CAS有3个操作数,位置内存值V,旧的预期值A,要修改的更新值B,如果内存值V和预期值相同则,内存值改为B,否则什么都不做。当它重来重试的这种行为称为-自旋。

CAS是一条cpu的原子指令,不会造成所谓的数据不一致问题。CAS是靠硬件实现的从而在硬件层面提升效率,最底层还是交给硬件来保证原子性和可见性。

1.3.案例代码

1.4 unsafe类

在java中CAS操作的执行依赖于Unsafe类的方法,其中Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存

意unsafe类中的所有方法都是native修饰的,也就是说unsafe类中的方法都直接调用操作系统底层资源,执行相应任务。

AtomicInteger类主要利用CAS+voltile和native方法来保证原子操作,从而避免synchronized的高开销,执行效率大为提升。

CAS的全程compare-and-swap,它是一条cpu并发原语,它的功能是判断内存某个位置是否为预期值,如果是则更改为新的值,这个过程是原子的。

1.5 cas的缺点问题 

1.循环时间开销大

2.ABA问题

举例:线程x从内存位置V中取出A,这个时候另一个线程y也从内从中取出A,并且线程y进行了一些操作将值变成了B,然后线程y又将V位置的数据变成A,这个时候线程x进行CAS操作发现内存中仍然是A,预期ok,然后线程x操作成功。

1.6 手写自旋锁案例代码

package com.ljf.thread.cas;

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

/**
 * @ClassName: CasDemo
 * @Description: TODO
 * @Author: admin
 * @Date: 2023/12/16 00:04:06 
 * @Version: V1.0
 **/
public class CasDemo {
    AtomicReference<Thread> atomicReference=new AtomicReference<>();
    public static void main(String[] args) {
     CasDemo casDemo=new CasDemo();
     new Thread(()->{
         casDemo.lock();
         try {
             TimeUnit.SECONDS.sleep(5);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         casDemo.unlock();
     },"A").start();
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            casDemo.lock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            casDemo.unlock();
        },"B").start();
    }
    public void lock(){
        Thread thread=Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"\t......come in");
        while(!atomicReference.compareAndSet(null,thread)){

        }
    }
    public void unlock(){
        Thread thread=Thread.currentThread();
      atomicReference.compareAndSet(thread,null);
      System.out.println(Thread.currentThread().getName()+"\t......task over,unlock....");
    }
}

结果:

二  自旋锁

2.1 自旋锁的概念

CAS是实现自旋锁的基础,CAS利用CPU指令保证了操作的原子性,以达到锁的效果,至于自旋呢,就是尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁。当线程发现锁被占用时,会不断循环判断锁的状态,直到获取锁。这样做的好处是减少线程上下文切换的消耗。缺点是循环会消耗cpu。

解决办法是使用:版本号或者时间戳

2.2 案例ABA问题demo

package com.ljf.thread.cas;

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

/**
 * @ClassName: Zixuan
 * @Description: TODO
 * @Author: admin
 * @Date: 2023/12/15 20:55:32 
 * @Version: V1.0
 **/
public class Zixuan {
    static AtomicInteger atomicInteger=new AtomicInteger(100);
   static AtomicStampedReference<Integer> atomicStampedReference=new AtomicStampedReference<Integer>(100,1);
    public static void main(String[] args) {
        new Thread(()->{
            int stamp=atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t"+"首次版本号:"+stamp);
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t 2次流水号:"+atomicStampedReference.getStamp());
            atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t 3次流水号:"+atomicStampedReference.getStamp());
        }).start();
       new Thread(()->{
           int stamp=atomicStampedReference.getStamp();
           System.out.println(Thread.currentThread().getName()+"\t"+"首次版本号:"+stamp);
           try {
               TimeUnit.SECONDS.sleep(1);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
          boolean b=atomicStampedReference.compareAndSet(100,2002,stamp,stamp+1);
           System.out.println(b+" "+atomicStampedReference.getReference()+"\t :"+atomicStampedReference.getStamp());
       },"t4").start();

    }
    public static void abaCompare(){
        new Thread(()->{
            atomicInteger.compareAndSet(100,101);
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicInteger.compareAndSet(101,100);
        },"t1").start();
        //
        new Thread(()->{
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(atomicInteger.compareAndSet(100,2022)+"\t"+atomicInteger.get());
        },"t2").start();
    }
}

结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值