JUC-CAS

news/2024/5/20 6:51:00 标签: juc

1. CAS概述

CAS(Compare ans swap/set) 比较并交换,实现并发的一种底层技术。它将预期的值和内存中的值比较,如果相同,就更新内存中的值。如果不匹配,一直重试(自旋)。Java.util.concurrent.atomic包下的原子类都使用了CAS算法

2. CAS原理

CAS具体的操作是将预期的值和内存中真实的值进行比较,如果相同就更新值。如果不相同就重试(自旋)。
CAS是通过Unsafe的compareAndSwap方法实现的,底层实现是CPU原子指令cmpxchg,不会造成数据不一致的问题。
CAS依靠底层硬件实现的无锁原子算法。比synchronized重量级锁性能更好。

3. CAS与自旋锁

3.1 前置知识:原子引用类AtomicReference

将自定义的类型变成原子类,能够进行cas操作。

    public static void main(String[] args)
    {
        AtomicReference<User> atomicReference = new AtomicReference<>();
        User z3 = new User("z3",22);
        User li4 = new User("li4",28);
        atomicReference.set(z3);
        System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.get().toString());
        System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.get().toString());
    }

3.2 CAS实现一个自旋锁:A线程持有锁,B线程自旋等待直到A释放锁。

/**
 * 题目:实现一个自旋锁,复习CAS思想
 * 自旋锁好处:循环比较获取没有类似wait的阻塞。
 *
 * 通过CAS操作完成自旋锁,A线程先进来调用myLock方法自己持有锁5秒钟,B随后进来后发现
 * 当前有线程持有锁,所以只能通过自旋等待,直到A释放锁后B随后抢到。
 */
public class SpinLockDemo
{
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void lock()
    {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"\t"+"----come in 等待锁");
        while (!atomicReference.compareAndSet(null, thread)) {

        }
        System.out.println(Thread.currentThread().getName() + "\t 拿到锁");
    }

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

    public static void main(String[] args)
    {
        SpinLockDemo spinLockDemo = new SpinLockDemo();

        new Thread(() -> {
            spinLockDemo.lock();
            //暂停几秒钟线程
            try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
            spinLockDemo.unLock();
        },"A").start();

        //暂停500毫秒,线程A先于B启动
        try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
            spinLockDemo.lock();

            spinLockDemo.unLock();
        },"B").start();


    }
}

核心思想:

  • 加锁:开始的时候,内存中的值为NULL,一旦有线程进入,预期值为NULL,和真实值匹配将内存设置为当前线程值。(此时其他线程进来后预期值NULL,但是真实值已经被修改所以会自旋,这为加锁)
  • 解锁: 释放锁之后,重新设置内存的值为NULL,让其他线程能够进入从而修改

运行结果:
在这里插入图片描述

CAS和synchronized比较

CAS线程不会阻塞,线程一直自旋。
Synchronized会阻塞,会进行线程的上下文切换非常耗费资源。

CAS缺点

循环时间长开销大

CAS如果预测值和真实值不一样,将一直自旋。导致循环CPU开销大。

ABA问题

ABA问题顾名思义就是将线程1在进行CAS操作的时候,另一个线程2已经将A修改为B又快速修改回A,导致一个线程并未发现中间修改过仍能正常进行CAS比较和修改。

ABA问题解决方案

原子时间戳引用:给每次修改都加上一个时间戳(版本号),CAS操作的时候不仅要比较预测值和真实值,还要比较预测版本号和真实版本号。

new AtomicStampedReference().compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp)

CAS注意事项

CAS只能保证原子性,不能保证变量的可见性。要配合volatile使用,保证共享变量的可见性。
CAS适用于并发量不高,多核CPU的情况。并发量增高,CAS自旋会导致消耗CPU资源。这时候用LongAdder


http://www.niftyadmin.cn/n/5345827.html

相关文章

GO——GPM

参考&#xff1a;https://juejin.cn/post/6844904130398404616 并发模型 参考&#xff1a;https://zhuanlan.zhihu.com/p/137339439 多进程 要点 主进程监听每进来一个请求&#xff0c;fork子进程处理 缺点 进程占用高&#xff0c;服务器负载高进程间通信困难 参考&#xff…

新建虚拟机并安装配置linux

记得看目录哦&#xff01; 1. 打开VMware2. 新建虚拟机3. 配置虚拟机内存4. 配置处理器5. 配置网络6. 开始安装CentOS系统系统安装包此处自取 7. 启动虚拟机进行安装如果出现这种情况这样转成图形化界面继续安装 1. 打开VMware 点击文件—点击新建虚拟机 2. 新建虚拟机 3. 配置…

antv/g6绘制数据流向图

antv/g6绘制数据流向图 前言接口模拟数据htmlts页面效果 前言 在业务开发中需要绘制数据流向图&#xff0c;由于echarts关系图的限制以及需求的特殊要求&#xff0c;转而使用antv/g6实现&#xff0c;本文以代码的方式实现数据流向需求以及节点分组,版本"antv/g6": “…

【云原生】Docker如何构建镜像

目录 前言 一、基于已有的镜像创建 步骤一&#xff1a;先基于现有的镜像创建一个容器&#xff0c;然后进入容器去完成修改 步骤二&#xff1a;将该容器作为一个模板提交创建为一个新的镜像 步骤三&#xff1a;基于新的镜像&#xff0c;docker run创建一个容器&#xff0c;进…

源码篇--Redisson 分布式锁lock的实现

文章目录 前言一、Redisson 分布式锁的实现&#xff1a;1.1 引入redis 和 redisson jar1.2 redis 客户端配置&#xff1a;1.3 业务实现&#xff1a; 二、Redisson lock 实现原理2.1 lock.lock()&#xff1a;2.2 锁释放 lock.unlock()&#xff1a; 总结 前言 我们知道Redis 缓存…

@JsonFormat失效,被jackson自定义配置覆盖

jackson配置类 我的jackson配置类如下&#xff0c;其中serializerByType(LocalDateTime.class, new LocalDateTimeSerializer()) 覆盖了JsonFormat注解 Configuration public class JacksonConfiguration {public static final DateTimeFormatter optionalDateTimePattern (n…

Acwing787归并排序 788逆序对数量

theme: channing-cyan 归并排序 归并排序是一种分治算法&#xff0c;将待排序的数组递归地分成两半&#xff0c;分别排序&#xff0c;然后将两个有序的子数组归并成一个有序数组。将数组不断地二分&#xff0c;直到子数组的长度为1时&#xff0c;认为其有序。然后将相邻的两个…

【Java万花筒】云端之旅:Java容器化、微服务与云服务之道

Java云航&#xff1a;容器、Kubernetes、AWS、Spring Cloud与HttpClient深度探析 前言 随着云计算的迅猛发展&#xff0c;Java在构建云原生应用和微服务架构方面扮演着关键角色。本文将深入探讨Java在容器化、Kubernetes集群、AWS云服务、Spring Cloud微服务和Apache HttpCli…