首页 > 编程知识 正文

c++ 自旋锁,cas锁原理

时间:2023-05-03 17:58:58 阅读:129221 作者:2076

CAS统称Compare and swap (比较与交换),CAS自由锁定机制是一种乐观锁定,也称为自旋锁定。 CAS假设数据一般不冲突,因此在提交和更新数据时正式检测有无数据冲突,如果发现冲突则再次重复,在无限循环中进行比较,直到冲突消失为止。

Atomic类是采用CAS自由锁定机制实现的类;

CAS操作取决于底层硬件的CAS命令。 CAS命令包括检测和更新冲突两个步骤,但这两个步骤组合在一起是原子操作。

CAS命令需要三个操作数:

需要更新的变量(v )主存储器)旧的期待值) e )本地存储器)新的值)执行b ) CAS指令时,首先比较存储器位置v )主存储器)中的值和e )本地存储器)中的值是否相等(冲突检测执行流程如下图所示

从JDK 1.5开始,引入了原子类。 这些都以自动开头。 就AtomicInteger类而言,较低级别的运行原理如下图所示。

原子类的一般方法如下

//原子地将值与当前值相加,以便可以用于线程中的计数。 (返回更新后的值。 intaddandget(intdelta ) /可以原子地将当前值与值相加,用于线程中的计数。 (返回上一个值) intgetandadd(intdelta ) /作为原子对当前值加1 (返回当前值) (返回更新值) int incrementAndGet ) /作为原子对当前值加1 (返回上一个值) int getandince ) ) )将原子设置为给定值(返回旧值) intgetandset(intnewvalue ) /将原子减1 (返回更新值) /将原子设置为当前值

package com.test; import Java.util.concurrent.executorservice; import Java.util.concurrent.executors; import Java.util.concurrent.atomic.atomic integer; /*原子系统测试*/publicclassatomicthreadtest {//通常的int类型static int intNum=0; //原子系统staticatomicintegernum=newatomicinteger (0; publicstaticvoidmain (字符串[ ] args )//线程数int threadNum=10; executorserviceexecutorservice=executors.newcachedthreadpool (; //为运算制作多个线程的for(intj=0; j threadNum; j ) { executorservice.execute (new runnable ) { @ override public void run } } { for (inti=0; i 1000; I ) )//如果每次将数字设为1,则相当于i num.incrementAndGet (; intNum; }}; //用于关闭线程池的命令。 如果不接受新任务,并且原始任务运行完成,请执行线程池executorService.shutdown (; System.out.println; System.out.println (常规int类型的结果:“intNum”)

看上面的图和例子还很抽象,请看下面的图。 详细说明了三个线程的执行顺序和内部活动的具体执行情况。

CAS问题CAS可以有效解决原子操作,但仍然存在三个问题

ABA问题周期长,开销大,只能保证一个共享变量的原子操作1、ABA问题是检查cas更新时值是否发生了变化,如果没有变化就不会更新,但一个值原来是a、b,又变成了a

ABA问题如何解决?

/strong>

ABA的问题的解决思路是使用版本号 version,每次变量更新的时候把版本号 加一,那么 A-B-A 就会变成 A1-B2-A3;可以使用AtomicStampedReference 来解决ABA问题,AtomicStampedReference 可以存放对象,在对象内增加一个version的属性即可;数据库的乐观锁也是用version版本号解决的,在表内增加一个version的字段,每次更新数据时 version + 1, 更新前先比较原版本号是否一致;sql语句如下:

update table set data = '更新的数据' , version = version + 1 where version = 1   2、循环时间长开销大

自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。

 

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

当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。

 

cas本身具备原子性吗?

答:cas本身是具备原子性,cas本身锁的操作是在hotSpot源码中实现的,也就是c++层面的,在hotSpot中的cas逻辑中有这么一行代码:

#define LOCK_IF_MP(mp)

这句代码是什么意思呢? 意思是如果你有好多个cpu,就执行如下代码:

lock cmpxchg

这个代码意思是lock compare exchange ,锁住这个cpu的北桥信号,属于硬件级别的锁;

但如果只有一个CPU,就只执行以下代码:

cmpxchg

cas底层上锁流程图如下

 

 

 

 

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。