常见锁策略以及CAS

目录

1.1乐观锁&悲观锁

1.2轻量级锁&重量级锁

1.3自旋锁&挂起等待锁

1.4互斥锁&读写锁 

1.5可重入锁&不可重入锁

1.6公平锁&非公平锁

1.7synchronized的特点

2.CAS(Compare and swap) 

2.1.是什么 

2.2.基于CAS方式实现的线程安全优缺点 

2.3.使用场景


1.1乐观锁&悲观锁

两类锁,是在锁冲突的概率上进行区分的

乐观锁指的是预测锁竞争不是很激烈(做的工作相对少一些),悲观锁预测锁竞争会很激烈。

悲观锁认为多个线程访问同一个共享变量冲突的概率较大 , 会在每次访问共享变量之前都去真正加锁. 乐观锁认为多个线程访问同一个共享变量冲突的概率不大. 并不会真的加锁 , 而是直接尝试访问数据. 在访问的同时识别当前的数据是否出现访问冲突 . 悲观锁的实现就是先加锁( 比如借助操作系统提供的 mutex), 获取到锁再操作数据 . 获取不到锁就 等待. 乐观锁的实现可以引入一个版本号. 借助版本号识别出当前的数据访问是否冲突

1.2轻量级锁&重量级锁

是从锁开销的角度区分的

轻量级锁加锁解锁开销比较小,效率更高.重量级锁加锁解锁开销比较大,效率更低.

多数情况下,乐观锁也是一个轻量级锁,悲观锁也是一个重量级锁。

1.3自旋锁&挂起等待锁

自旋锁 :是典型的轻量级锁,是一种忙等的锁,当一个线程尝试获取自旋锁时,如果锁已被其他线程占用,该线程会一直循环检测(自旋)直到锁可用为止,而不是被挂起等待。自旋锁适用于锁的持有时间较短或者线程并发度较低的情况,因为忙等待会消耗CPU资源。

第一次获取锁失败 , 第二次的尝试会在极短的时间内到来。一旦锁被其他线程释放, 就能立即获取到锁,线程会一直持有该锁直到释放。忙等消耗CPU换来了更快的响应速度。

挂起等待锁 :是典型的重量级锁,指当一个线程尝试获取锁时,如果锁已被其他线程占用,则该线程将会被挂起(即阻塞,阻塞的开销很大),直到锁可用为止。当锁可用时,系统会唤醒被挂起的线程继续执行。挂起等待锁可以减少CPU资源的消耗,适合于锁的持有时间较长或者线程竞争激烈的情况。

  • 如果锁的竞争频率高、持有时间短,可以考虑使用自旋锁,避免线程频繁切换和挂起带来的开销。
  • 如果锁的竞争不频繁、持有时间较长,可以考虑使用挂起等待锁,避免浪费CPU资源。

1.4互斥锁&读写锁 

  1. 互斥锁

    • 互斥锁是最基本的同步工具,用于保护临界区(Critical Section),确保同一时刻只有一个线程可以访问共享资源,避免多个线程同时修改数据导致数据不一致。
    • 当一个线程获取了互斥锁后,其他线程需要等待该线程释放锁才能访问被保护的资源。
    • 互斥锁会引起线程阻塞,因为其他线程可能会竞争锁而处于忙等状态。

1.针对写加锁,在数据库事务的脏读中提起过,脏读就是一个事务读取到了另一个事务未提交的数据,采用了写加锁,写的时候不能进行读。
2.针对读加锁,在不可重复读中提起,指一个事务多次读取同一数据,而另一事务对该数据进行了修改,导致两次读取到的结果不一致,采用读加锁,读取的时候不能进行写。


多线程针对同一个变量并发读是没有线程安全问题的.也不需要加锁.

读写锁就是把读操作和写操作分别进行加锁 .
读锁和读锁之间不互斥 .
写锁和写锁之间互斥 .
写锁和读锁之间互斥 .
读写锁最主要用在 " 频繁读 , 不频繁写 " 的场景中


假设一组线程并发读同一个变量,这时线程之间是没有锁竞争的,也没有线程安全问题!假设一组线程有读又有写,才会产生锁竞争..实际开发中,读操作非常高频

  • 如果读操作非常频繁,而写操作较少,并且读操作之间没有依赖关系,那么读写锁可能是更好的选择。

1.5可重入锁&不可重入锁

一个线程针对同一把锁连续加锁两次如果不会出现死锁就是可重入锁,sychronized就是可重入锁,其中引入计数器,并对锁的持有线程进行身份标识,当发现当前加锁的线程就是持有锁的线程就直接进行+1操作。

1.6公平锁&非公平锁

 主要区别在于获取锁的顺序以及线程等待锁时的公平性。

  • 公平锁:注重顺序

    • 公平锁是指多个线程按照它们请求锁的顺序获取锁,即先到先得。也就是说,当一个线程请求获取锁时,如果锁已经被其他线程占用,该线程会被放入等待队列,并在等待队列中按照先后顺序等待获取锁。
    • 公平锁保证了所有线程获取锁的公平性,避免了某些线程长时间无法获取锁的情况,确保每个线程都有机会获取锁。
  • 非公平锁:注重性能synchronized

    • 非公平锁允许多个线程竞争锁,线程获取锁的顺序不再受到先后顺序的限制。当一个线程请求获取锁时,如果锁已经被其他线程占用,该线程可能会直接尝试获取锁,而不必进入等待队列。
    • 非公平锁的优点在于可以提高系统的吞吐量,减少线程切换的开销。但是可能会导致某些线程长时间无法获取锁,出现“饥饿”现象。

1.7synchronized的特点

上述就是锁的基本特点,通过上述特点我们来描述synchronized的特点:

1.synchronized既是一个悲观锁,又是个乐观锁,自适应的!
   synchronized默认是乐观锁,但是如果发现锁竞争比较激烈,就会变成悲观锁!!
2.synchronized既是轻量级锁,又是一个重量级锁,自适应!
   synchronized默认是轻量级锁,当锁冲突剧烈后,就变成重量级锁!
3.synchronized这里的轻量级锁是基于自旋锁的方式实现的
  synchronized这里的重量级锁是基于挂起等待锁的方式实现的


4.synchronized不是读写锁
5.synchronized是可重入锁
6.synchronized是非公平锁

2.CAS(Compare and swap

2.1.是什么 

通常用于实现乐观锁。CAS 操作包括三个步骤:比较、更新和写入。比较和交换的是内存和寄存器。CAS是一个CPU指令。一个CPU指令就可以完成上述操作,单个CPU指令是原子的即线程安全,可以使用cas操作完成一些操作进而代替“加锁”。CAS是CPU提供的一个特殊指令,通过这个指令,就可以一定程度的处理线程安全问题!

boolean CAS(V,A,B){if(A == V){V = B;return true;}return false;
}
//像是赋值
全称 Compare and swap, " 比较并交换 ". 相当于通过一个原子的操作 , 同时完成 " 读取内存 , 比 较是否相等, 修改内存 " 这三个步骤 . 本质上需要 CPU 指令的支撑

2.2.基于CAS方式实现的线程安全优缺点 

基于CAS方式实现的线程安全(无锁编程):

 优点:保证线程安全避免阻塞;

 缺点:代码复杂,只适合特定场景

CAS 操作是原子性操作,它可以保证在多线程环境下对共享数据的操作是线程安全的。如果 CAS 操作失败(即预期值和内存位置的值不相等),通常会进行重试或执行其他逻辑来处理并发冲突。在 Java 中,CAS 操作通常通过 java.util.concurrent.atomic 包中的原子类来实现,例如 AtomicIntegerAtomicLongAtomicReference 等。这些原子类提供了一系列 CAS 操作方法,如 compareAndSet()getAndIncrement()getAndSet() 等,让开发者能够方便地利用 CAS 机制实现线程安全的操作。

CAS 的优点包括无锁化、减少线程切换开销、提高并发度等;缺点则包括 ABA 问题、循环重试导致性能消耗等。因此,在使用 CAS 时需要注意处理可能出现的问题,并结合具体场景进行合理的选择。

2.3.使用场景

CAS本质上是CPU提供的指令,又被系统封装提供成api,然后被JVM封装成api,我们就可以使用了。

实现原子类
标准库中提供了 java.util.concurrent.atomic , 里面的类都是基于这种方式来实现的 .
典型的就是 AtomicInteger . 其中的 getAndIncrement 相当于 i++ 操作 .
public class Test {public static void main(String[] args) throws InterruptedException {AtomicInteger count = new AtomicInteger(0);Thread t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {count.getAndIncrement();//count++}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {count.getAndIncrement();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count.get());}
}

getAndIncrement归于unsafe里面,因为稍有不慎就可能会出错。compareAndSwapInt为native方法即在JVM源码中通过c++实现。

public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);
}||\/public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));return var5;}||\/public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

伪代码实现原子类:

class AtomicInteger {private int value;public int getAndIncrement() {int oldValue = value;while ( CAS(value, oldValue, oldValue+1) != true) {//通过重试判定了是否有穿插,oldValue = value;}return oldValue;}
}
//原子类基于CAS实现,synchronized通过阻塞,

自旋锁

public class SpinLock {private Thread owner = null;public void lock(){// 通过 CAS 看当前锁是否被某个线程持有. // 如果这个锁已经被别的线程持有, 那么就自旋等待. // 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程. while(!CAS(this.owner, null, Thread.currentThread())){}}public void unlock (){this.owner = null;}
}

 ABA问题

CAS操作中通过之没有发生变化作为没有其他线程穿插执行的判定。ABA问题是指在并发编程中,一个共享变量的值从 A 变成 B,再从 B 变成 A,期间可能会引发一些意外情况。这种情况通常发生在使用 CAS(Compare and Swap)操作时。

举个例子来说明ABA问题:

  1. 线程 T1 读取共享变量的值为 A。
  2. 线程 T2 将共享变量的值由 A 修改为 B,然后再修改回 A。
  3. 线程 T1 再次检查共享变量的值,发现仍然是 A,认为共享变量的值没有发生变化。

在上面的过程中,尽管共享变量经历了从 A 到 B,再到 A 的变化,但是线程 T1 并没有意识到这个过程,因此可能导致一些潜在的问题。

CAS 操作在读取旧值的同时 , 也要读取版本号 .
真正修改的时候 ,
如果当前版本号和读到的版本号相同 , 则修改数据 , 并把版本号 + 1.
如果当前版本号高于读到的版本号 . 就操作失败 ( 认为数据已经被修改过了).

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

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

相关文章

设计模式----工厂模式

工厂模式 工厂模式即建立创建对象的工厂&#xff0c;实现创建者和调用者分离。 简单工厂模式&#xff1a;该模式对对象创建管理方式最为简单&#xff0c;因为他简单的对不同类对象的创建进行了一层薄薄的封装。该模式通过向工厂传递类型来指定要创建的对象。 工厂方法模式&am…

JVM对象的创建流程与内存分配

对象的创建流程与内存分配 创建流程对象内存分配方式内存分配安全问题对象内存分配流程【重要】:对象怎样才会进入老年代?重点 案例演示:对象分配过程大对象直接进入老年代02-对象内存分配的过程: 创建流程 加载 验证 解析 准备 初始化 使用 写在 对象内存分配方式 内存分配…

GPT-SoVITS-WebUI 克隆声音 macos搭建

强大的少样本语音转换与语音合成Web用户界面 macos运行参考 macos conda create -n GPTSoVits python3.9 conda activate GPTSoVits激活环境 conda activate GPTSoVits停用 conda deactivate mkdir GPTSoVits cd GPTSoVits git clone https://github.com/RVC-Boss/GPT-SoVITS…

算法项目(1)—— LSTM+CNN+四种注意力对比的股票预测

本文包含什么? 项目运行的方式(包教会)项目代码(在线运行免环境配置)不通注意力的模型指标对比一些效果图运行有问题? csdn上后台随时售后.项目说明 本项目实现了基于CNN+LSTM构建模型,然后对比不同的注意力机制预测股票走势的效果。首先看一下模型结果的对比: 模型MS…

2024年2月的TIOBE指数,go语言排名第8,JAVA趋势下降

二月头条&#xff1a;go语言进入前十 本月&#xff0c;go在TIOBE指数前10名中排名第8。这是go有史以来的最高位置。当谷歌于2009年11月推出Go时&#xff0c;它一炮而红。在那些日子里&#xff0c;谷歌所做的一切都是神奇的。在Go出现的几年前&#xff0c;谷歌发布了GMail、谷歌…

枚举类(enum)

优质博文&#xff1a;IT-BLOG-CN ​ 枚举类&#xff1a; 就是对象的实例个数是确定的&#xff08;例如&#xff1a;单例模式&#xff09;&#xff0c;也就说我们在创建枚举类的时候&#xff0c;会对构造器进行设置 一、自定义创建枚举类 为什么需要枚举类&#xff1f; 【1】…

我国无水氢氟酸产量逐渐增长 东岳集团市场占比较大

我国无水氢氟酸产量逐渐增长 东岳集团市场占比较大 无水氢氟酸是一种十分重要的化工产品&#xff0c;在常温常压下多表现为一种无色发烟液体。无水氢氟酸具有吸水性强、化学活性高、介电常数高、阻燃性能好等优点。经过多年发展&#xff0c;无水氢氟酸制备方法已经成熟&#xf…

Spring Cloud Alibaba-04-Sentinel服务容错

Lison <dreamlison163.com>, v1.0.0, 2023.09.10 Spring Cloud Alibaba-04-Sentinel服务容错 文章目录 Spring Cloud Alibaba-04-Sentinel服务容错高并发带来的问题服务雪崩效应常见容错方案Sentinel入门什么是Sentinel微服务集成Sentinel安装Sentinel控制台 实现一个接…

【前端】前端三要素之BOM

写在前面&#xff1a;本文仅包含BOM内容&#xff0c;JavaScript传送门在这里&#xff0c;DOM传送门在这里。 本文内容是假期中刷的黑马Pink老师视频&#xff08;十分感谢Pink老师&#xff09;&#xff0c;原文保存在个人的GitLab中&#xff0c;如果需要写的网页内容信息等可以评…

QT设置窗口随窗体变化(窗口文本框随窗体的伸缩)

目录 1.建立新窗口2.最终效果 1.建立新窗口 1&#xff09;在窗体中创建一个 textBrowser&#xff0c;记录坐标及宽高 X-100 Y-130 宽-571 高-281&#xff0c;窗体宽高800*600&#xff1b; 2&#xff09;在.h头文件中插入void resizeEvent(QResizeEvent *event) override;函数 …

挑战!贪吃蛇小游戏的实现(3)

经过&#xff08;1&#xff09;&#xff08;2&#xff09;两篇文章的介绍&#xff0c;相信大家对该游戏的实现已经有了具体的思路&#xff0c;废话不多说&#xff0c;让我们开始实现相关的代码吧&#xff01; 1.游戏主逻辑 void test() {int ch 0;srand((unsigned int)time(NU…

【Unity3D】ASE制作天空盒

找到官方shader并分析 下载对应资源包找到\DefaultResourcesExtra\Skybox-Cubed.shader找到\CGIncludes\UnityCG.cginc观察变量, 观察tag, 观察代码 需要注意的内容 ASE要处理的内容 核心修改 添加一个Custom Expression节点 code内容为: return DecodeHDR(In0, In1);outp…

在Win系统部署WampServer并实现公网访问本地服务【内网穿透】

目录 推荐 前言 1.WampServer下载安装 2.WampServer启动 3.安装cpolar内网穿透 3.1 注册账号 3.2 下载cpolar客户端 3.3 登录cpolar web ui管理界面 3.4 创建公网地址 4.固定公网地址访问 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0…

企业资产|企业资产管理系统|基于springboot企业资产管理系统设计与实现(源码+数据库+文档)

企业资产管理系统目录 目录 基于springboot企业资产管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户信息管理 2、用户审核管理 3、资产分类管理 4、资产信息管理 5、资产信息添加 6、资产借出统计 7、资产归还审核 8、资产维修管理 9、资产维修…

c++笔记理解

1.封装 &#xff08;1&#xff09;构造函数不是必须在的 可以通过行为修改属性 &#xff08;2&#xff09;private和protected区别在于继承那里要学 &#xff08;3&#xff09;类默认是私有&#xff0c;struct是共有 私有的好处&#xff1a;控制数据的有效性&#xff0c;意…

MySQL 8.0.36 WorkBench安装

一、下载安装包 百度网盘链接&#xff1a;点击此处下载安装文件 提取码&#xff1a;hhwz 二、安装&#xff0c;跟着图片来 选择Custom,然后点Next 顺着左边框每一项的加号打开到每一个项的最底层&#xff0c;点击选中最底层的项目&#xff0c;再点击传过去右边的绿色箭头&a…

Codeforces Round 530 (Div. 2)

CF1099A Snowball 题目 有一个重量为 w 的雪球正在高度为 h 的地方向下滚动。每秒它的高度会减少 1。同时在高度 i 的位置它的重量会增加 i&#xff08;包括初始位置&#xff09; 同时在滚动的路线上有 2 块石头&#xff0c;第 i 块石头的高度为 hi​&#xff0c;即雪球会在 hi…

【论文阅读|基于 YOLO 的红外小目标检测的逆向范例】

基于 YOLO 的红外小目标检测的逆向范例 摘要1 引言2 相关工作2.1 逆向推理2.2 物体检测方法 3 方法3.1 总体架构3.2 逆向标准的可微分积分 4 实验4.1 数据集和指标4.2 实验环境4.4 OL-NFA 为少样本环境带来稳健性 5 结论 论文题目&#xff1a; A Contrario Paradigm for YOLO-b…

详解 leetcode_078. 合并K个升序链表.小顶堆实现

/*** 构造单链表节点*/ class ListNode{int value;//节点值ListNode next;//指向后继节点的引用public ListNode(){}public ListNode(int value){this.valuevalue;}public ListNode(int value,ListNode next){this.valuevalue;this.nextnext;} }package com.ag; import java.ut…

[树形DP] 最长乘积链

题目 1.最长乘积链 - 蓝桥云课 (lanqiao.cn) 初始思路 对问题进行分析&#xff0c;对每个点dfs去求走不同路的最远距离与次远距离求乘积&#xff0c;时间复杂度为O(n^2) 看了答案怎么弄的优化 解题思路 总的来说 预处理&#xff08;对每个结点的信息进行统计&#xff09…