常见的锁策略CAS

news2024/11/16 7:48:27

目录

一、乐观锁&悲观锁

1.1、悲观锁

1.2、乐观锁

二、重量级锁&轻量级锁

2.1、轻量级锁 

2.2、重量级锁

三、自旋锁&挂机等待锁

3.1、自旋锁 

3.2、挂起等待锁 

四、读写锁&普通互斥锁

4.1、读写锁

4.2、互斥锁 

五、公平锁&非公平锁

六、可重入锁&不可重入锁 

6.1、可重入锁

6.2、不可重入锁

七、对比synchronized

八、CAS

8.1、什么是CAS 

8.2、CAS应用

8.2.1、实现原子类

8.3、CAS的ABA问题


一、乐观锁&悲观锁

1.1、悲观锁

在获取锁的时候预期这个锁的竞争十分激烈,那么就必须先加锁再执行任务,阻塞其它想获取锁的任务。

1.2、乐观锁

在获取锁的时候预期这个锁的竞争不太激烈,那么就可以先不加锁,或者少加锁(有真实的竞争的时候再加锁)

举个栗子:

坤坤要在西安开演唱会,小黑子练习时长两年半好不容易抢到了一张咯咯的演唱会门票去看咯咯的演唱会,就在开场10分钟后,小黑子觉得肚子不舒服想要去厕所方便一下到了厕所这个点没几个人来上厕所,小黑子就没有给厕所门上锁,当他听见有脚步声才把门插上。

二、重量级锁&轻量级锁

锁的核心特性”原子性“,这样的机制追根溯源是CPU这样的硬件设备提供的。

  • CPU提供了"原子操作指令".
  • 操作系统基于CPU的原子指令,实现了mutex互斥锁.
  • JVM基于操作系统提供的互斥锁,实现了synchronized和ReentrantLock等关键字和类.
  • 注意:synchronized并不仅仅是对mutex进行封装,在synchronized内部还做了其它很多工作。

2.1、轻量级锁 

轻量级锁的加锁过程比较简单,用到的资源比较少,典型的就是用户态的一些加锁操作(在java代码层面就可以完成加锁).

  • 少量的内核态用户态切换.
  • 不太容易引发线程调度.

2.2、重量级锁

重量级锁的加锁过程比较复杂,用到的资源也比较多,典型的就是内核态的一些加锁操作.

  • 大量的内核态用户态切换.
  • 很容易引发线程调度.

乐观锁是能不加就不加锁,导致消耗的资源也就少了,所以乐观锁也是轻量级锁。

悲观锁是能加就加锁,导致消耗的资源也就多了,所以悲观锁也是重量级锁。

三、自旋锁&挂机等待锁

3.1、自旋锁 

自旋锁伪代码:

while (抢锁(lock) == 失败){} 

如果获取锁失败,立即尝试获取锁,无限循环,直到获取锁为止,第一次获取锁失败,第二次的尝试会在极短的时间内到来。所以一旦锁资源被释放,就能够第一时间获取到锁。 

自旋锁是一种典型的轻量级锁实现方式. 

  • 优点:没有放弃CPU,不涉及线程阻塞和调度,一旦锁被释放,就能第一时间获取到锁。
  • 缺点:如果锁被其它线程持有的时间比较久,那么就会持续消耗CPU资源.(而挂起等待锁是不需要消耗CPU的) 

synchronized中的轻量级锁策略大概就是通过自旋锁的方式实现的. 

3.2、挂起等待锁 

不主动访问锁资源,而是让系统调度去竞争锁资源。

  1. 通过阻塞与就绪状态的切换来获取锁资源.
  2. 锁一旦被释放,没有办法立刻知道.
  3. 是通过系统内核来处理的. 

挂起等待锁是一种典型的重量级锁实现方式.

四、读写锁&普通互斥锁

4.1、读写锁

多线程之间,数据的读取方之间不会产生线程安全问题,但数据的写入方之间以及和读者之间都需要进行互斥。如果两种场景下都使用同一个锁,那么就会产生极大的性能损耗,所以产生了读写锁。

读写锁:

在读的时候加读锁(共享锁),多个锁可以共存,同时加多个读锁是互不影响的。

在写的时候加写锁(排他锁),只有一个写锁在执行任务时,和别的锁是冲突的。

  • 写锁和写锁不能共存.
  • 写锁和读锁不能共存.
  • 读锁和读锁可以共存. 

读写锁就是把读操作和写操作区分对待,java库中提供了ReentrantReadWriteLock 类, 实现了读写
锁. 

  • ReentrantReadWriteLock.readLock类表示一个读锁,这个对象提供了lock()/unlock方法进行加锁。
  • ReentrantReadWriteLock.writeLock类表示一个写锁,这个对象也提供了lock()/unlock方法进行加锁。

读写锁特别适合于"频繁读,不频繁写"的场景中.

synchronized不是读写锁 

4.2、互斥锁 

有竞争关系,只能一个线程释放锁之后,别的线程再来抢。 

五、公平锁&非公平锁

5.1、公平锁

公平锁讲究的守规矩,遵循”先来后到“的原则,先排队的线程先获取到锁,后排队的线程后获取到锁。

5.2、非公平锁

大家都去抢锁,谁抢到就是谁的。

注意:

  • 操作系统内部的线程调度就可以视为随机调度,如果不作任何额外的限制,锁就是非公平锁,如果想要实现公平锁,就需要依赖额外的数据结构,来记录线程顺序。
  • 公平锁和非公平锁没有好坏之分,关键还要看适用场景。 

synchronized是公平锁 

六、可重入锁&不可重入锁 

6.1、可重入锁

可重入锁可以对一把锁连续加锁多次,而不造成死锁。加锁多次那么解锁也要多次解锁,否则其它线程就无法获取到锁。

java中只要以Reentrant开头的都是可重入锁,而且JDK提供的所有的现成的lock实现类,包括synchronized都是可重入的

6.2、不可重入锁

不可重入锁就是对一把锁连续加锁多次,造成死锁。linux系统提供的mutex是不可重入锁。

七、对比synchronized

八、CAS

8.1、什么是CAS 

CAS:全称Compare and swap,意思就是”比较并交换“,一个CAS涉及到以下操作。

假设内存中的源数据V,旧的预期值是A,需要修改的新值为B。

1、比较A与V是否相等(比较)。

2、如果比较相等,将B写入A(交换)。

3、返回操作是否成功。 

举个栗子:

 场景:

泡了一杯茶,泡上了之后,有事需要出去一趟。

十分钟回来之后,看了一下茶,如果茶还是满杯和走之前是一样的,那么就代表没有人喝过,就可以继续喝。

如果这个茶剩半杯了,就代表有人喝过了,那么就不能喝了,再重新泡一杯。

 

执行CAS操作重要的是确定有没有其它线程修改过第一次获取的值,如果确定没有被修改过,那么当前线程去修改是通过一条CPU指令去完成的,那么修改的过程就是线程安全的。 

8.2、CAS应用

8.2.1、实现原子类

标准库中提供了 java.util.concurrent.atomic 包, 里面的类都是基于这种方式来实现的.
典型的就是 AtomicInteger 类. 其中的 getAndIncrement 相当于 i++ 操作.

AtomicInteger atomicInteger = new AtomicInteger(0);
// 相当于 i++
atomicInteger.getAndIncrement();

假设两个线程同时调用 getAndIncrement
1) 两个线程都读取 value 的值到 oldValue 中. (oldValue 是一个局部变量, 在栈上. 每个线程有自己的栈) 

2) 线程1 先执行 CAS 操作. 由于 oldValue 和 value 的值相同, 直接进行对 value 赋值.
注意:

  •  CAS 是直接读写内存的, 而不是操作寄存器.
  • CAS 的读内存, 比较, 写内存操作是一条硬件指令, 是原子的.

  

3) 线程2 再执行 CAS 操作, 第一次 CAS 的时候发现 oldValue 和 value 不相等, 不能进行赋值. 因此需要进入循环,在循环里重新读取 value 的值赋给 oldValue

4) 线程2 接下来第二次执行 CAS, 此时 oldValue 和 value 相同, 于是直接执行赋值操作.

  

5) 线程1 和 线程2 返回各自的 oldValue 的值即可.

通过形如上述代码就可以实现一个原子类. 不需要使用重量级锁, 就可以高效的完成多线程的自增操作.

示例代码:


import java.util.concurrent.atomic.AtomicInteger;

//CAS原子类
public class Exe_01 {
    public static void main(String[] args) throws InterruptedException {
        //基于CAS的原子类
        AtomicInteger atomicInteger=new AtomicInteger();
        Thread t1=new Thread(() ->{
            for (int i = 0; i < 50000; i++) {
                //通过调用并获取自增的方法实现自增操作。
                atomicInteger.getAndIncrement();
            }
        });
        Thread t2=new Thread(() ->{
            for (int i = 0; i < 50000; i++) {
                //通过调用并获取自增的方法实现自增操作。
                atomicInteger.getAndIncrement();
            }
        });
        //启动线程
        t1.start();
        t2.start();
        //等待两个线程结束
        t1.join();
        t2.join();
        //打印执行结果
        System.out.println("执行结果——》"+atomicInteger.get());
    }
}

 

8.3、CAS的ABA问题

刚才的场景:

1、泡好茶出门了,十分钟又回来了,按照之前的逻辑,检查杯子中的水满着没有,如果满着就代表没有人动过,可以继续喝。

2、但是存在一个问题,中途有人喝了一半之后,又续上了,回来一看还是这杯水,这杯水跟走之前那杯水完全不一样了。 

前面看到的结果(A)和后面看到的结果(A)是一样的,但是中间发生过改变(B),只不过这个值跟取值之前是一样的。 

如何解决这个问题?

这个值加一个属性,记录一下修改的次数(版本号),这个值只增不减,只要这个值做了修改就+1.

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

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

相关文章

HBase(7):大量数据的计数统计

当HBase中数据量大时&#xff0c;可以使用HBase中提供的MapReduce程序来进行计数统计。语法如下&#xff1a; $HBASE_HOME/bin/hbase org.apache.hadoop.hbase.mapreduce.RowCounter 表名 1 启动YARN集群 启动yarn集群 start-yarn.sh 启动history server mr-jobhistory-da…

计算机视觉:多通道卷积操作

本文重点 前面我们学习了对灰度图的卷积操作(二维图像),本节课程我们学习RGB 彩色图像的卷积操作(三维立体)也就是说现在我们不仅想检测灰度图像的特征,也想检测 RGB 彩色图像的特征。 彩色图片的表示方法 彩色图片通常使用RGB(Red、Green、Blue)三个颜色通道来表示…

【裸机开发】UART 串口通信(一)—— 寄存器解析

目录 一、认识 UART 1、概念 2、帧格式 二、IO 复用为 UART 寄存器解析 1、原理图分析 2、寄存器解析 三、UART 相关寄存器解析 1、UART1_UCR1~4 2、UART1_USR1~2 3、波特率配置 4、UART1_URXD 5、UART1_UTXD 一、认识 UART 1、概念 UART 是一种通用的串行、异步…

web自动化测试如何实现(二)

目录 主流的自动化方案 web自动化测试环境如何搭建 1.安装selenium &#x1f381;更多干货 完整版文档下载方式&#xff1a; 主流的自动化方案 怎么进行选择&#xff1a; 如果有前端开发基础&#xff1a;cypress 如果只打算测试web端&#xff1a;playwright 除此之外&a…

【运维工程师学习】Linux常用命令

Linux常用命令 验证系统版本验证系统类型&#xff08;32位or64位&#xff09;验证分区情况验证CPU配置验证内存配置文件操作1、pwd2、cd3、ls 如何像将ll定义为ls -l的别名&#xff0c;这样去设置定义别名文件操作&#xff08;mkdir、rm、cp、mv&#xff09;文本编辑&#xff0…

Spring专家课程Day03_SpringMVC概述

文章目录 一、SpringMVC 项目搭建1、SpringMVC 简介2、SpringMVC的核心组件和执行流程3、SpringMVC的项目创建&#xff08;Idea&#xff09;3.1 Maven中创建 二、Thymeleaf试图_显示注册试图三、控制器接收表达数据四、接受get请求参数_RequestParm&#xff1b;总结&#xff1a…

2023年网络安全比赛--Web渗透测试国赛篇(超详细)

一、竞赛时间 180分钟 共计3小时 二、竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 1.获取Apache的版本号作为Flag值(例如:5.2.14)提交; 2.获取Samba服务器的版本号作为Flag值(例如:5.0.22)提交; 3.获取系统的内核版本号作为Flag值(例如:2.6.18)提交 4.网站根…

计算机网络 - 第一章(上)

1.1_1 概念及功能_哔哩哔哩_bilibili1.1_1 概念及功能是王道计算机考研 计算机网络的第2集视频&#xff0c;该合集共计76集&#xff0c;视频收藏或关注UP主&#xff0c;及时了解更多相关视频内容。https://www.bilibili.com/video/BV19E411D78Q?p2&spm_id_frompageDriver&…

提高 pyecharts 生成的网页的加载速度

使用 Pyecharts 生成的可视化作品是 HTML 的形式&#xff0c;需要使用特定的 js 代码。如果生成完全离线可用的文件&#xff0c;文件里会包含大量的 js 代码&#xff0c;文件会过大不利于分享。如果生成没有 js 代码的文件&#xff0c;则默认在 pycharts.org 上加载相应的 js&a…

十一、云尚办公系统-微信公众号

云尚办公系统&#xff1a;微信公众号 B站直达【为尚硅谷点赞】: https://www.bilibili.com/video/BV1Ya411S7aT 本博文以课程相关为主发布&#xff0c;并且融入了自己的一些看法以及对学习过程中遇见的问题给出相关的解决方法。一起学习一起进步&#xff01;&#xff01;&…

处理机调度

在多道程序环境下&#xff0c;内存中存在着多个进程&#xff0c;进程的数目往往多于处理机的数目。这就要求系统能按某种算法&#xff0c;动态地将处理机分配给一个处于就绪状态的进程&#xff0c;使之执行。分配处理机的任务是由处理机调度程序完成的。 对于大型系统运行时的…

Vue事件处理@传参

错误写法&#xff1a;将showInfo()函数直接写在vm外面。 <body><div id"root"><h2>欢迎来到{{name}}大学</h2><!--v-on: 当。。。。的时候click 点击事件时候去找showInfo这个函数--><button v-on:click"showInfo">…

图灵学院:用 Explain 查看 SQL 的执行计划

文章目录 一、Explain 概述1.1 Explain 含义及作用1.2 Explain 的基本用法 二、Explain 返回列详解2.1 数据准备2.2 id 列2.3 select_type 列2.3.1 simple2.3.2 primary2.3.3 subquery2.3.4 dependent subquery2.3.5 derived2.3.6 union2.3.7 dependent union2.3.8 union resul…

【保姆级】Redis安装教程(Windows版)

Redis安装教程&#xff08;Windows版&#xff09; 文章目录 Redis安装教程&#xff08;Windows版&#xff09;1.下载安装包2. 安装注意事项3. 注意事项4. 登录Redis客户端5. 停止Redis服务附&#xff1a;详细安装步骤附&#xff1a;RESP&#xff08;Redis桌面管理&#xff09;使…

掌握Tampermonkey,让网页玩出新花样

掌握Tampermonkey&#xff0c;让网页玩出新花样 何为Tampermonkey?Tampermonkey有何神通&#xff1f;操作示例 今天我要向朋友们介绍一个超酷的浏览器插件&#xff0c;Tampermonkey。我把它称之为一根神奇的魔法棒&#xff0c;可以让你对网页的玩法、样式和功能实现自定义。 熟…

windows环境下安装zookeeper

安装 下载地址&#xff1a;Apache Downloads 注意&#xff1a;zookeeper的安装路径不要有中文&#xff0c;建议也不要有空格 文件路径如下&#xff1a; 生成并修改zoo.cfg文件 复制zookeeper的conf目录下的zoo_simple.cfg文件&#xff0c;并重命名为zoo.cfg 修改zoo.cfg文件…

nx.draw报错 ‘_AxesStack‘ object is not callable

文章目录 前言解决办法1、关掉梯子&#xff01;&#xff01;&#xff01;2、更新pip3、更新networkx库和matplotlib库4、再次执行代码画图成功 总结 前言 用Networkx画图时报错&#xff1a; ‘_AxesStack‘ object is not callable。 解决办法 1、关掉梯子&#xff01;&#…

项目计划工具:自动生成项目周期计划的利器,写方案项目计划再也不需要为计算工期而烦恼了

在项目管理中&#xff0c;制定一个合理的项目计划是确保项目顺利进行的关键。然而&#xff0c;对于复杂的项目来说&#xff0c;手动编制项目计划表往往会非常耗时且容易出错。幸运的是&#xff0c;现代项目管理工具的出现解决了这个问题。本文将介绍一种强大的项目计划工具&…

网卡突然自动关闭

故障现象&#xff1a;主机突然不通&#xff0c;登录服务器看网卡的状态是down 解决方案&#xff1a; 1、尝试重启网卡&#xff0c;发现不行&#xff0c;干脆重启服务还是不行 service network restart reboot ifup eth0 #报错如下2、根据报错上网搜了下&#xff0c;猜测网络…

解决bug:Multiple assets emit different content to the same filename index.html

问题描述 同事将他的代码发给我&#xff0c;我下载依赖并用npm run serve运行项目过程中&#xff0c;出现Conflict: Multiple assets emit different content to the same filename index.html的报错 原因分析&#xff1a; 可能是文件在创建打包过程中&#xff0c;文件路径有中…