java多线程 CAS和自旋锁和unsafe总结

本文详细介绍了Java中的CAS(比较并替换)和自旋锁的概念,以及Unsafe类的功能。CAS是乐观锁的一种实现方式,通过CPU的原子操作保证线程安全,但存在ABA问题和循环开销大的缺点。自旋锁在持有锁时间短的情况下能提高性能,但当锁竞争激烈时可能导致CPU资源浪费。Unsafe类提供了底层内存操作,包括内存分配、对象实例化、字段访问和多线程同步等功能,但使用需谨慎。
摘要由CSDN通过智能技术生成

目录

CAS简介

CAS 是怎么实现线程安全的?

CAS的可见性与重排序

CAS与内核态

CAS的实现

CPU如何实现原子操作

解密CAS底层指令

CAS的缺点 

ABA问题

循环时间长开销大的问题

只能保证一个共享变量的原子操作

CAS的应用

JUC包下的原子类是如何用CAS实现的

乐观锁在项目开发中的实践,有么?

那开发过程中ABA你们是怎么保证的?

自旋锁

自旋锁简介

自旋锁优点

自旋锁缺点

java实现自旋锁例子

Unsafe

Unsafe简介

Unsafe的功能


注意:本文参考  《吊打面试官》系列-乐观锁、悲观锁

一文彻底搞懂CAS实现原理 - 知乎

Java 面试题:什么是自旋锁,有什么优缺点 - 知乎

用Java原子变量的CAS方法实现一个自旋锁 - LaplaceDemon - 博客园

说一说Java的Unsafe类 - pkufork - 博客园

CAS简介

CAS(Compare And Swap 比较并且替换)是乐观锁的一种实现方式,是一种轻量级锁,JUC 中很多工具类的实现就是基于 CAS 的。

CAS 是怎么实现线程安全的?

线程在读取数据时不进行加锁,在准备写回数据时,先去查询原值,操作的时候比较原值是否修改,若未被其他线程修改则写回,若已被修改,则重新执行读取流程。

举个栗子:现在一个线程要修改数据库的name,修改前我会先去数据库查name的值,发现name=“帅丙”,拿到值了,我们准备修改成name=“三歪”,在修改之前我们判断一下,原来的name是不是等于“帅丙”,如果被其他线程修改就会发现name不等于“帅丙”,我们就不进行操作,如果原来的值还是帅丙,我们就把name修改为“三歪”,至此,一个流程就结束了。

有点懵?理一下停下来理一下思路。

Tip:比较+更新 整体是一个原子操作,当然这个流程还是有问题的,我下面会提到。

他是乐观锁的一种实现,就是说认为数据总是不会被更改,我是乐观的仔,每次我都觉得你不会渣我,差不多是这个意思。

CAS的可见性与重排序

AbstractQueuedSynchronizer: compareAndSetState(int expect,int update)

该方法以原子操作的方式更新state变量,本文把Java的compareAndSet()方法调用简称为 CAS。JDK文档对该方法的说明如下:如果当前状态值等于预期值,则以原子方式将同步状态 设置为给定的更新值。此操作具有volatile读和写的内存语义。

这里我们分别从编译器和处理器的角度来分析,CAS如何同时具有volatile读和volatile写 的内存语义。

注意:这里的state是一个volatile的变量!

前文我们提到过编译器不会对volatile读与volatile读后面的任意内存操作重排序;编译器不会对volatile写与volatile写前面的任意内存橾作重排序。组合这两个条件,意味着为了同时实现volatile读和volatile写的内存语义编译器不能对CAS与CAS前面和后面的任意内存操作重排序。

 如何实现的?

常见的intel X86处理器中,这个本地方法在openjdk中依次调用的c++代码为:

unsafe.cpp, atomic.cpp和atomic一windows一x86.inline.hpp

 程序会根据当前处理器的类型来决定是否为cmpxchg指令添加lock前 缀。如果程序是在多处理器上运行,就为cmpxchg指令加上lock前缀(Lock Cmpxchg)。反之,如 果程序是在单处理器上运行,就省略lock前缀(单处理器自身会维护单处理器内的顺序一致性,不需要lock前缀提供的内存屏障效果)。

intel的手册对lock前缀的说明如下。

1)确保对内存的读-改-写操作原子执行。在Pentium&Pentium之前的处理器中,带有lock前 缀的指令在执行期间会锁住总线,使得其他处理器暂时无法通过总线访问内存。很显然,这会 带来昂贵的开销。从Pentium4、Intel Xeon及P6处理器开始,Intel使用缓存锁定(Cache Locking) 来保证指令执行的原子性。缓存锁定将大大降低lock前缀指令的执行开销。

2)禁止该指令,与之前和之后的读和写指令重排序。

3)把写缓冲区中的所有数据刷新到内存中。

上面的第2点和第3点所具有的内存屏障效果,足以同时实现volatile读和volatile写的内存语义。

CAS与内核态

CAS相当于在用户态代码里边插入了一个cmpxchg指令

直观看大概是这个样子:用户态内存空间[...你的代码你的代码cmpxchg你的代码你的代码...]

这样CPU一直在用户态执行,执行到cmpxchg指令也不是说就是切换内核态了,切换到内核态可以这么理解:就是CPU开始执行了内核态内存空间的操作系统的代码。

总结,CAS是没有发生用户态到内核态的切换的。只是在用户态执行了cmpxchg指令而已(这个指令由硬件保证原子性,所谓不可再分的CPU同步原语)。而执行指令要比上下文切换的开销要小,所以CAS要比重量级互斥锁性能要高。

然后说下重量级锁,直观看大概是这个样子:用户态空间[...你的代码你的代码你的代码lock] -> 执行操作系统内核态代码获得互斥锁(高低电位锁总线balabala)、返回互斥锁给用户态代码 -> 用户态空间[获得锁 你的代码你的代码...]

这样上述过程很显然发生了用户态到内核态切换了。

CAS的实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值