JUC(10):CAS、各种锁的理解

CAS(是一种 自旋锁)

什么是CAS?

CAS,Compare And Swap,释义比较并替换。CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该 位置的值。CAS也称为自旋锁,在一个(死)循环【for(;;)】里不断进行CAS操作,直到成功为止(自旋操作),实际上,CAS也是一种乐观锁

悲观锁和乐观锁

  • 悲观锁:具有强烈的独占和排他特性,悲观锁总是假设最坏的情况,每次取数据时都会认为其他线程会修改。
  • 乐观锁:相对悲观锁而言,乐观锁总是假定最好的情况,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制。
    sync也是一种独占锁,而独占锁正是悲观锁的表现。独占锁,如读锁,写锁,数据库的行级锁和表级锁。它会引起其他需要同一把锁的线程挂起,等待当前持有锁资源的线程释放。

CAS实现原子操作的三大问题

  • ABA问题:其他线程把共享变量从A改成了B,很快又改回了A,CAS检查时看起来没有变化,实际上产生了变化。解决这个问题使用版本号,在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。同时,Java并发包为了解决这个问题,提供了一个带有标记的原子引用类“AtomicStampedReference”,它可以通过控制变量值的版本来保证CAS的正确性。因此,在使用CAS前要考虑清楚“ABA”问题是否会影响程序并发的正确性,如果需要解决ABA问题,改用传统的互斥同步可能会比原子类更高效。
  • 循环时间长开销大:自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。可以看到getAndAddInt方法执行时,如果CAS失败,会一直进行尝试。如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。
  • 只能保证一个共享变量的原子操作:当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。如:AtomicReference ar = new AtomicReference<>();

Java层面的CAS

 //CAS:compareAndSet :比较并交换!
    public static void main(String[] args) {
        AtomicInteger aT = new AtomicInteger(2020);//初始值
        //public final boolean compareAndSet(int expectedValue, int newValue)
        //参数分别是:期望、更新
        //如果我期望的值达到了,就更新,否则就不更新
        aT.compareAndSet(2020,2021);//因为这里的期望值达到了,所以更新;如果没达到就输出初始值!
        System.out.println(aT.get());
    }

在这里插入图片描述
将初始值改为200
在这里插入图片描述

各种锁的理解

公平锁与非公平锁

公平锁:不能插队,必须先来后到!
非公平锁:可以插队。(默认的锁都是非公平:Synchronized、Lock)

可重入锁

可重入锁又叫递归锁。广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。ReentrantLock和synchronized都是可重入锁。

自旋锁

自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成busy-waiting。

它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,”自旋”一词就是因此而得名。

死锁排查

参考文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值