什么是 CAS(自旋锁)? 它的优缺点? 如何使用CAS实现一把锁?

news2024/11/27 22:43:59

什么是自旋锁?

CAS
没有获取到锁的线程是不会阻塞的,通过循环控制一直不断的获取锁。

CAS: Compare and Swap,翻译成比较并交换。 执行函数 CAS(V,E,N)

CAS 有 3 个操作数,内存值 V,旧的预期值 E,要修改的新值 N。当且仅当预期值 E 和内存值 V 相同时,将内存值 V 修改为 N,否则什么都不做

在这里插入图片描述

  1. Cas 是通过硬件指令,保证原子性
  2. Java 是通过 unsafe jni 技术

原子类: AtomicBoolean,AtomicInteger,AtomicLong 等使用 CAS 实现。

优缺点

优点:没有获取到锁的线程,会一直在用户态,不会阻塞,没有锁的线程会一直通过循环控制重试。

缺点:通过死循环控制,消耗 cpu 资源比较高,需要控制循次数,避免 cpu 飙高问题。

CAS实现一把锁逻辑

Cas 无锁机制原理:

  1. 定义一个锁的状态;
  2. 状态状态值=0 则表示没有线程获取到该锁;
  3. 状态状态值=1 则表示有线程已经持有该锁;

实现细节:
CAS 获取锁:
将该锁的状态从 0 改为 1-----能够修改成功 cas 成功则表示获取锁成功
如果获取锁失败–修改失败,则不会阻塞而是通过循环(自旋来控制重试)

CAS 释放锁:
将该锁的状态从 1 改为 0 如果能够改成功 cas 成功则表示释放锁成

完整代码

/**
 * @author zyz
 * @version 1.0
 * @data 2023/7/17 15:09
 * @Description: 使用CAS实现一把锁
 *               测试
 *                  1、上锁不使用循环实现
 *                     1):获取锁、释放锁, 结果有些获取成功,有些获取失败
 *                     2):获取锁,不释放锁  结果:只有一个线程获取锁成功,其它都失败
 *                  2、上锁使用循环实现
 *                     1) 获取锁、释放锁, 全部成功。
 *                     2) 获取锁,不释放锁, 结果:只有一个线程获取成功,其它线程一直循环等待。CPU飙高
 */
public class UsingCasHandwritingLock {
    private AtomicLong cas = new AtomicLong(0);
    private Thread lockCurrentThread;

    /**
     * 获取锁
     * 锁是有状态的
     * 0 --- 表示没有人持有该锁
     * 1 --- 表示该锁已经被线程持有
     * 获取成功:cas 0变为1 cas = true
     * 获取锁失败 cas false
     */
    public Boolean tryLock() {
        boolean result = cas.compareAndSet(0, 1);
        return result;
    }

    /**
     *:优点:没有获取到锁的线程,会一直在用户态,不会阻塞,没有锁的线程会一直通过循环控制重试。
     * 缺点:通过死循环控制,消耗 cpu 资源比较高,需要控制循次数,避免 cpu 飙高问题;
     * @return
     */
//    public Boolean tryLock(){
//        while (true){
//            System.out.println(Thread.currentThread().getName() + ",CAS");
//            boolean result = cas.compareAndSet(0,1);
//            if(result){
//                lockCurrentThread = Thread.currentThread();
//                return true;
//            }
//        }
//    }

    /**
     * 释放锁
     */
    public Boolean unLock() {
        if (lockCurrentThread != Thread.currentThread()) {
            return false;
        }
        return cas.compareAndSet(1, 0);

    }

    public static void main(String[] args) {
        UsingCasHandwritingLock lock = new UsingCasHandwritingLock();
        IntStream.range(1, 10).forEach((i) -> new Thread(() -> {
            try {
                boolean result = lock.tryLock();
                if (result) {
                    lock.lockCurrentThread = Thread.currentThread();
                    System.out.println(Thread.currentThread().getName() + ",获取锁成功~~~");
                } else {
                    System.out.println(Thread.currentThread().getName() + ",获取锁失败~~~");
                }

            } catch (Exception ex) {

            } finally {
                if (lock != null) {
                    lock.unLock();
                }

            }
        }).start());
    }
}

测试结果

1、释放锁(非循环获取锁)

    //获取锁
    public Boolean tryLock() {
        boolean result = cas.compareAndSet(0, 1);
        return result;
    }

正常释放锁

   try {
   //做的任务
   } catch (Exception ex) {
   //抛异常
   } finally {
   //释放锁
       if (lock != null) {
           lock.unLock();
       }
   }

结果:有些线程获取锁成功,有些线程获取锁失败。

说明:线程获取到该锁,并且还在执行任务,未到释放锁的时候。其它线程在获取锁的时候,就会出现获取锁失败的情况。

在这里插入图片描述

2、不释放锁(非循环释放锁)

    //获取锁
    public Boolean tryLock() {
        boolean result = cas.compareAndSet(0, 1);
        return result;
    }

将释放锁的过程取消,获取到该锁的线程一直拿着。

   try {
   //做的任务
   } catch (Exception ex) {
   //抛异常
   } finally {
   //释放锁
   //    if (lock != null) {
   //        lock.unLock();
   //    }
   }

结果:只有一个线程获取锁成功,其它线程获取锁均失败

说明:某个线程获取到该锁,并且一直拿着,不释放。其它线程就不能获取到锁。

在这里插入图片描述

3、释放锁(循环获取锁)

    public Boolean tryLock(){
        while (true){
            System.out.println(Thread.currentThread().getName() + ",CAS");
            boolean result = cas.compareAndSet(0,1);
            if(result){
                lockCurrentThread = Thread.currentThread();
                return true;
            }
        }
    }

当前线程执行完任务后,释放锁

   try {
   //做的任务
   } catch (Exception ex) {
   //抛异常
   } finally {
   //释放锁
       if (lock != null) {
           lock.unLock();
       }
   }

结果:线程全部获取到锁。

说明:未获取到锁的线程,通过循环的形式一直尝试获取锁。只要有线程释放了锁。其它线程就去尝试获取锁,获取不到锁的线程,就一直循环尝试获取。直到获取到锁为止。

在这里插入图片描述

4、不释放锁 (循环获取锁)

    public Boolean tryLock(){
        while (true){
            System.out.println(Thread.currentThread().getName() + ",CAS");
            boolean result = cas.compareAndSet(0,1);
            if(result){
                lockCurrentThread = Thread.currentThread();
                return true;
            }
        }
    }

线程执行完任务后,不释放锁

   try {
   //做的任务
   } catch (Exception ex) {
   //抛异常
   } finally {
   //释放锁
   //    if (lock != null) {
   //        lock.unLock();
   //    }
   }

效果:只有一个线程获取到锁,其它线程一直循环尝试获取锁。
说明:当一个线程获取到锁之后,其它线程就一直循环尝试获取锁。但是该线程没有释放锁,就会导致其它线程一直循环尝试获取锁,就会导致CPU飙高。
在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/764078.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【天梯赛集训】7.17习题集

AC&#xff1a; 12 / 12 用时&#xff1a;2 h 21 min 没卡思路&#xff0c;卡了几个测试点。 7-1 输入输出整数 #include <iostream>using namespace std;int main() {int a;cin >> a;cout << a;return 0; } 7-2 调整数组使奇数全部都位于偶数前面其他数字顺…

Anaconda的python虚拟环境中安装cudatoolkit和cudnn加速tensorflow

1. 背景 由于本地安装了cuda 10.0, 但是现在需要在Anaconda中安装不同的python虚拟环境来安装tensorflow-gpu、对应的cudatoolkit、对应的cudnn来加速&#xff0c;下面是具体的演示流程 2. 安装 我这里以安装tensorflow-gpu1.9.0为例&#xff0c;首先进入python的虚拟环境&a…

多元函数的混合偏导数

定理&#xff1a;多元函数的混合导数相等。 直接看图&#xff1a; 引自知乎&#xff1a;点击跳转知乎链接

IIS部署WCF的文件夹要加上IIS_USERS的权限

弯路1&#xff0c;文件夹没加权限报错&#xff1a; 报错如图&#xff1a; 弯路2&#xff1a;多网卡多IP&#xff0c;要设置固定IP。样式&#xff1a; http://192.168.1.4:8080/Service1.svc

RabbitMQ实现六类工作模式

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; RabbitMQ实现六类工作模式 ⏱️ 创作时间&#xff1a; 2023年07月20日…

Jenkins持续集成自动化测试

目录 执行集成构建 持续&#xff0c;自动地构建&测试软件项目代码管理&#xff08;git/svn&#xff09;>编译&#xff08;maven/ant/gradle&#xff09;>打包>测试环境部署>自动化测试 研发体系中的迭代流程 1 源码分支管理&#xff1a; git或者svn, 将不同开…

C++基础与深度解析02——

0. 前言 接上文C基础与深度解析01&#xff0c;本篇主要介绍C的输入输出流&#xff0c;如下 1. 基础概念 1.1头文件 通常&#xff0c;在一个 C 程序中&#xff0c;只包含两类文件—— .cpp 文件和 .h 文件。其中&#xff0c;.cpp 文件被称作 C 源文件&#xff0c;里面放的都是…

【C++】STL——vector的有关空间的函数介绍和使用、size和capacity函数、resize和reserve函数

文章目录 1.vector的使用2.vector空间增长问题&#xff08;1&#xff09;size 获取数据个数&#xff08;2&#xff09;capacity 获取容量大小&#xff08;3&#xff09;empty 判断是否为空&#xff08;4&#xff09;resize 改变vector的size&#xff08;5&#xff09;reserve 改…

Alvas.Audio v2019 Crack

Alvas.Audio v2019 Crack 该库使C#和VB.Net程序员能够创建执行&#xff08;包括混合声音信息&#xff09;、捕获、转换和编辑音频的应用程序。 阿尔瓦斯。音频是C#音乐库。网络程序员。 这使你能够生产。NET程序&#xff0c;例如Winforms/WPF/Windows服务/控制台录音机、Int…

❤️创意网页:使用CSS和HTML创建令人惊叹的3D立方体

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…

经典文献阅读之--SRIF-based LiDAR-IMU Localization(SRIF的LiDAR-IMU自动驾驶鲁棒定位)

0. 简介 对于车辆来说&#xff0c;我们更希望能够得到一个有效的定位系统&#xff0c;能够保证高精度的同时&#xff0c;拥有较高的鲁棒性&#xff0c;而《Robust SRIF-based LiDAR-IMU Localization for Autonomous Vehicles》就是这样一篇文章&#xff0c;在各种场景中实现了…

起名大师,支持多种取名方式,根据自己的喜好去选择

软件功能&#xff1a; 1.参考宝宝姓氏、性别、生辰八字、天格、地格辅助用户为宝宝取名。 2.一次可生产数千个好名字&#xff0c;您还可根据笔画数、拼音、五行等筛选喜欢的名字。 3.提供10余种方法供起名选择&#xff0c;比如指定取名&#xff0c;谐音取名&#xff0c;生日取…

【百度】判断ip地址是否合法

在LeetCode上没有看到这个题目&#xff0c;加上对String的API记得不清楚&#xff0c;导致这个题目没有写得很好&#xff0c;许愿面试官能够仁慈一点 一个合法的ip地址应该有&#xff1a; 三个点将字符串划分为4个数字数字的大小[0,255]&#xff0c;且数字不能为空 合理应用St…

mycat 垂直分库与水平分表使用详解

说明 在了解mycat的常用分片规则之前,有必要再对涉及到分片规则相关的几个配置文件做深入的了解,包括:schema.xml,server.xml,rule.xml等, 其中最核心的schema.xml文件是配置分片规则的入口文件,有必要对该配置文件中的关键参数做了解,且看下面这幅图,回顾下里面的配置…

【C++】二叉搜索树KV模型

最典型的一个场景&#xff0c;自动翻译软件&#xff0c;输入中文&#xff0c;输出对应的英文&#xff0c;输入英文&#xff0c;输出对应的中文。 可以用一颗搜索二叉树来实现这一功能。 K->key V->val 基础结构和普通搜索二叉树保持一致&#xff0c;只是成员多了一个_val…

关于Tab制表符,点击一次跳很多字符的问题解决

首先在出现问题的地方右键鼠标&#xff0c;出现后点击段落。 进入后点击左下角的制表位 进入后点击全部清除&#xff0c;然后确认&#xff0c;问题就解决了&#xff08;哪里有问题就处理哪里&#xff09;

天纵竞赛系统助力江苏省“苏小登杯”不动产登记技能竞赛暨首届全国赛省级选拔赛

7月14日&#xff0c;第四届江苏省“苏小登杯”不动产登记技能竞赛暨首届全国赛省级选拔赛在苏州广播电视总成功举办。天纵竞赛系统提供核心软件技术及其配套硬件支持。 本次竞赛由江苏全省13支队伍、52名一线不动产登记人员参加比赛&#xff0c;竞赛环节包括笔试、现场竞答、代…

性能测试 —— JMeter分布式测试及其详细步骤

性能测试概要 性能测试是软件测试中的一种&#xff0c;它可以衡量系统的稳定性、扩展性、可靠性、速度和资源使用。它可以发现性能瓶颈&#xff0c;确保能满足业务需求。很多系统都需要做性能测试&#xff0c;如Web应用、数据库和操作系统等。 性能测试种类非常多&#xff0c…

windows操作小技巧1:文件批操作更改类型

今日更新一个Windows操作小技巧: 日常生活中我们有批量操作更改文件后缀名&#xff08;类型&#xff09;的需要&#xff1a; 比如这有五个.txt文本文件&#xff0c;我要想将其批量改为.html该如何操作呢&#xff1f; 首先新建文本文档&#xff1a; 其次在新建的文本文档输入以…

B 端软件:常见知识梳理

前言 我一直从事企业级软件研发工作&#xff0c;也就是我们通常称之为 B 端软件。近年来&#xff0c;我的工作重心主要在研发低代码平台和 aPaaS 平台&#xff0c;这使我对 B 端软件有了更深入的理解。 和 B 端软件对应的就是我们熟悉的 C 端软件&#xff0c;我们手机中安装的那…