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

发布时间:2023年12月17日

一? CAS作用介绍

1.1 CAS作用

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

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

2.案例代码

1.2 unsafe类

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

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

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

1.3?CAS的缺点

1.循环时间开销大

2.ABA问题

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

1.4?手写自旋锁

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

结果:

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