CAS机制的讲解以及实际项目中的使用

首先要明白cas解决的问题,它是乐观锁的一种解决方式,都是多线程并发情况下解决数据线程按全问题的一种手段-----无锁并发

为什么无锁效率高?

  • 无锁情况下,即使重试失败,线程始终在高速运行,没有停歌,而 synchronized 会让线程在没有获得锁的时候,发生上下文切换,进入阻塞。打个比喻
  • 线程就好像高速跑道上的赛车,高速运行时,速度超快,一旦发生上下文切换,就好比赛车要减速、熄火,等被唤醒又得重新打火、启动、加速... 恢复到高速运行,代价比较大
  • 但无锁情况下,因为线程要保持运行,需要额外CPU 的支持,CPU 在这里就好比高速跑道,没有额外的跑道,线程想高速运行也无从谈起,虽然不会进入阻塞,但由于没有分到时间片,仍然会进入可运行状态,还是会导致上下文切换。

CAS的特点

结合CAS和volatile 可以实现无锁并发,适用于线程数少、多核CPU的场景下。(cas要配合volatile使用,因为多线程操作下,为了保证线程修改完数据后,其他线程立刻可见)

  • CAS 是基于乐观锁的思想:最乐观的估计,不怕别的线程来修改共享变量,就算改了也没关系,我吃亏点再重试呗。
  • synchronized是基于悲观锁的思想: 最悲观的估计,得防着其它线程来修改共享变量,我上了锁你们都别想改,我改完了解开锁,你们才有机会。
  • CAS 体现的是无锁并发、无阻塞并发,请仔细体会这两句话的意思
  • 因为没有使用synchronized,所以线程不会陷入阻塞,这是效率提升的因素之一
  • 但如果竞争激烈,可以想到重试必然频繁发生,反而效率会受影响

下边列举几个java中使用cas实现的几个原子类

  • 首先是几个基本类型的原子操作类AtomicInteger,AtomicBoolean等,它们实现线程安全的方式就是通过一个compareAndSet方法,第一个参数就是当前线程操作时首先获取的值,第二个是修改以后的值,它会通过当前线程获取的值在准备修改前去和当前存储的值做对比,如果在它一开始拿到值以后,有别的线程修改了它,那么在修改前比对一开始获取的值和现在的值就会发现不同,那么这个方法就不会去修改,然后返回false,表示修改失败,如果我们想实现修改,就可以给这个方法外套一层循环,除非修改成功,跳出循环,否则一直循环尝试修改,这几个原子类中好多的方法都是通过这样的方式实现的修改功能,例如下边的getAndSet
    public final boolean getAndSet(boolean newValue) {boolean prev;do {prev = get();} while (!compareAndSet(prev, newValue));return prev;}
  • 然后是引用类型操作类AtomicReference,比如String,Double各种时间类型等表示单个值的引用类型都可以使用它实现无锁并发且线程安全,以下以String类型为例子实现引用类型的多线程线程安全
    import java.util.concurrent.*;
    import java.util.concurrent.atomic.AtomicReference;public class AtomicTest {static AtomicReference<String> atomicReference=null;public AtomicTest(String list){this.atomicReference=new AtomicReference<>(list);}public static void main(String[] args) throws InterruptedException {AtomicTest atomicTest = new AtomicTest("");CountDownLatch countDownLatch = new CountDownLatch(100);ExecutorService executorService = Executors.newFixedThreadPool(200);for (int i = 1; i <=100 ; i++) {executorService.submit(()->{try{atomicTest.update(String.valueOf(1));}finally {countDownLatch.countDown();}});}countDownLatch.await();executorService.shutdown();System.out.println(atomicReference.toString());System.out.println(atomicReference.get().length());}public   void update(String H){AtomicTest.atomicReference.updateAndGet(g->{return g+H;});}
    }
    

    注意它们里边只有使用了compareAndSet才能保证操作的原子性和线程安全

  • AtomicStampedReference--上边的AtomicReference存在一个问题,比如现在有一个String类型,当前线程首先获取到了a,但是现在有其他线程先把a改成了b,又把b改成了a,如果我们要求,只要其他线程修改过这个值,我们就要修改失败,那么AtomicReference是没办法实现的,在AtomicStampedReference并没有帮我们实现类似于updateAndGet这样的方法,需要我们自己通过compareAndSet实现,见如下代码

    import java.util.concurrent.*;
    import java.util.concurrent.atomic.AtomicStampedReference;public class AtomicTest {static AtomicStampedReference<String> atomicReference=null;public AtomicTest(String list){this.atomicReference=new AtomicStampedReference<>(list,0);}public static void main(String[] args) throws InterruptedException {AtomicTest atomicTest = new AtomicTest("");CountDownLatch countDownLatch = new CountDownLatch(100);ExecutorService executorService = Executors.newFixedThreadPool(200);for (int i = 1; i <=100 ; i++) {executorService.submit(()->{try{atomicTest.update(String.valueOf(1));}finally {countDownLatch.countDown();}});}countDownLatch.await();executorService.shutdown();System.out.println(atomicReference.getReference());System.out.println(atomicReference.getReference().length());}public   void update(String H){while (true){//获取最新值String reference = atomicReference.getReference();//获取版本号int stamp = atomicReference.getStamp();if (atomicReference.compareAndSet(reference,reference+H,stamp,stamp+1)){break;}}}
    }

  •  AtomicMarkableReference--上边的在解决AtomicReference存在的问题是通过版本号实现的,如果我们只是想实现,别的线程修改过,则无法修改,可以通过一个boolean即可实现,那么AtomicMarkableReference就是实现了这样功能,我在这里就不一一介绍了,大家自己研究一下

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

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

相关文章

QB/T 2658-2017 卫生设备用台盆检测

卫生设备用台盆根据盆体材质的不同分为陶瓷台盆&#xff0c;钢化玻璃台盆&#xff0c;玻璃纤维增强塑料台盆&#xff0c;不锈钢台盆。搪瓷台盆等&#xff0c;主要用于卫生间、公共设施等场合作为洗手、洗脸用台盆。 QB/T 2658-2017 卫生设备用台盆测试 测试项目 测试标准 外…

山西电力市场日前价格预测【2023-12-24】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-12-24&#xff09;山西电力市场全天平均日前电价为324.41元/MWh。其中&#xff0c;最高日前电价为456.41元/MWh&#xff0c;预计出现在18:00。最低日前电价为0.00元/MWh&#xff0c;预计出…

thinkphp+vue+mysql酒店客房管理系统 b1g8z

本系统包括前台界面、用户界面和管理员界面、员工界面。在前台界面里游客和用户可以浏览客房信息、公告信息等&#xff0c;用户可以预定客房&#xff0c;在用户中心界面里&#xff0c;用户可以管理预定信息&#xff0c;管理员负责用户预定的审核以及客房的发布、用户的入住等。…

【数据库设计和SQL基础语法】--连接与联接--多表查询与子查询基础(一)

一、引言 多表查询和子查询是数据库中强大的工具&#xff0c;用于在复杂数据结构中提取有价值的信息。其目的在于实现数据关联、筛选和汇总&#xff0c;使得用户能够更灵活地从多个表中检索所需的信息。这种查询方式的重要性体现在解决实际业务需求上&#xff0c;通过有效地组…

PHP开发案例:用PHP写一个简单的蜘蛛统计代码

在前面的文章中我们已经学习了怎么来识别蜘蛛(搜素引擎的爬虫),现在我们来运用我们学习到的知识写一个简单的程序。当然你必须在你需要统计的页面引入spider.php,否则是无法统计到的哦! 一、spider.php <?php function spider(){ $spider=0;//首先定义蜘蛛的默认值为…

Android开发中pcm格式的音频转换为wav格式之一

在我们开发中&#xff0c;会遇到pcm格式的音频转为wav&#xff0c;下面讲解一下具体怎么实现&#xff1a; 第一步&#xff1a;pcm文件转wav文件 /*** pcm文件转wav文件** param inFilename 源文件路径* param outFilename 目标文件路径* param deleteOrg 是否删除源文件*/…

要参加微软官方 Copilot 智能编程训练营了

GitHub Copilot 是由 GitHub、OpenAI 和 Microsoft 联合开发的生成式 AI 模型驱动的。 GitHub Copilot 分析用户正在编辑的文件及相关文件的上下文&#xff0c;并在编写代码时提供自动补全式的建议。 刚好下周要参加微软官方组织的 GitHub Copilot 工作坊-智能编程训练营&…

操作系统——进程管理算法和例题

1、概述 1.1 进程调度 当进程的数量往往多于处理机的个数&#xff0c;出现进程争用处理机的现象&#xff0c;处理机调度是对处理机进行分配&#xff0c;就是从就绪队列中&#xff0c;按照一定的算法&#xff08;公平、髙效&#xff09;选择一个进程并将处理机分配给它运行&am…

图像识别中的 Vision Transformers (ViT)

引言 Vision Transformers (ViT) 最近已成为卷积神经网络(CNN) 的竞争替代品&#xff0c;而卷积神经网络 (CNN) 目前在不同的图像识别计算机视觉任务中处于最先进的水平。ViT 模型在计算效率和准确性方面比当前最先进的 (CNN) 模型高出近 4 倍。 Transformer 模型已成为自然语…

【Vulnhub 靶场】【Corrosion: 1】【简单】【20210731】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/corrosion-1,730/ 靶场下载&#xff1a;https://download.vulnhub.com/corrosion/Corrosion.ova 靶场难度&#xff1a;简单 发布日期&#xff1a;2021年07月31日 文件大小&#xff1a;7.8 GB 靶场作者&#xf…

SpringBootSQL监控

零、人在地球 一个成熟的生产环境会存在很多sql&#xff0c;测试环境不能完全复现出生产环境的情况。因此我们需要一些数据监控内容 一般用于监控&#xff1a; ①一些重点sql是否正常&#xff08;某些极端数据导致查询异常&#xff09; ②一些跑批任务的执行结果 ③一些表…

【C++】bind绑定包装器全解(代码演示,例题演示)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Linux》…

WPF StackPanel

StackPanel是一个控件容器&#xff0c;它按照一个方向&#xff08;水平或垂直&#xff09;堆叠子元素&#xff0c;使得它们沿一个轴线对齐。你可以在StackPanel中放置其他控件&#xff0c;如按钮、标签、文本框、图片等等。这些控件的排列方式由StackPanel按照指定的方向自动确…

5.OpenResty系列之深入理解(一)

本文基于Centos8进行实践&#xff0c;请读者自行安装OpenResty。 1. 内部调用 进入默认安装路径 cd /usr/local/openresty/nginx/conf vim nginx.conflocation /sum {# 只允许内部调用internal;content_by_lua_block {local args ngx.req.get_uri_args()ngx.print(tonumber…

java进阶学习笔记

学习java深度学习&#xff0c;提升编程思维&#xff0c;适合掌握基础知识的工作者学习 1.反射和代理1.1 概念介绍1.2应用场景1.3 反射-reflect1.3.1 获得类-Class1.3.2 获得类的字段-Field1.3.3 动态访问和修改对象实例的字段1.3.4 获得类方法-Method1.3.5 调用方法.invoke1.3.…

评论回复功能数据库设计

1. 评论的场景 类似csdn博客评论 2. 建表sql CREATE TABLE comment (id varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT id,parent_id varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 父级评论id&#xff08;…

【Kafka每日一问】kafka三种压缩方式差别?

Kafka 提供了三种压缩算法&#xff0c;分别是GZIP、Snappy 和 LZ4。 这三种压缩算法的差异主要在以下方面&#xff1a; 压缩比&#xff1a;GZIP 压缩比最高&#xff0c;DEFLATE 算法&#xff0c;但压缩和解压缩速度相对较慢&#xff1b;Snappy 压缩比次之&#xff0c;但压缩和…

Kubernetes pod ip 暴露

1. k8s pod 和 service 网络暴露 借助 iptables 的路由转发功能&#xff0c;打通k8s集群内的pod和service网络&#xff0c;与外部网络联通 # 查看集群的 pod 网段和 service 网段 kubectl -n kube-system describe cm kubeadm-config networking:dnsDomain: cluster.localpod…

数据结构和算法笔记3:双指针法(快慢指针)

双指针法&#xff08;快慢指针法&#xff09;在数组、字符串和链表的操作中是非常常见的&#xff0c;这里结合力扣上的题进行可一下梳理&#xff0c;主要的思路是我们要明确快指针指的是什么&#xff0c;慢指针指的是什么。 1. 移除元素类问题 27. 移除元素 要我们移除目标元…

Vue2和Vue3组件间通信方式汇总(2)------$emit

组件间通信方式是前端必不可少的知识点&#xff0c;前端开发经常会遇到组件间通信的情况&#xff0c;而且也是前端开发面试常问的知识点之一。接下来开始组件间通信方式第二弹------$emit,并讲讲分别在Vue2、Vue3中的表现。 Vue2Vue3组件间通信方式汇总&#xff08;1&#xff0…