【Java】常见的锁策略及CAS

文章介绍了各种锁机制,包括乐观锁与悲观锁的概念,轻量级锁和重量级锁的区别,读写锁与互斥锁的使用场景,以及自旋锁和挂起等待锁的工作方式。此外,详细讨论了可重入锁和不可重入锁的特性,并提到了公平锁和非公平锁的差异。文章通过CAS(CheckandSwap)操作解释了如何实现原子类和自旋锁,同时也指出了CAS的ABA问题及其解决方案。
摘要由CSDN通过智能技术生成

常见的锁策略

乐观锁&悲观锁

描述的是一种态度
乐观锁:对运行环境持乐观的态度,刚开始不加锁,当有竞争的时候再去加锁;
悲观锁:对运行环境持悲观态度,刚开始就直接加锁。

轻量级锁&重量级锁

描述的是实现锁的过程,在实现锁的过程中,消耗的资源多不多。
轻量级锁:可以是纯用户态的锁,消耗的资源少。
重量级锁:可能会调用到系统的内核态,消耗的资源比较多。

读写锁&普通互斥锁

在现实中,并不是所有的锁都要互斥,互斥必然要消耗很多的资源。所以优化出了读写锁。
读锁:是一种共享锁,读与读可以同时拿到锁资源;
写锁:是一种排他锁,写与写、写与读、读与写不能同时存在。

自旋锁&挂起等待锁

自旋锁:不停的询问资源是否被释放,如果释放了,第一时间获取到锁资源。可以通过纯用户态实现。
挂起等待锁:等待通知后再去竞争锁,并不会第一时间获取锁到资源。

可重入锁&不可重入锁

可重入锁:对于同一个锁对象可以加多次锁;
不可重入锁:不能对同一个锁对象加多次锁。

公平锁&非公平锁

公平锁:先排队等待的线程先获取到锁资源;
非公平锁:没有先来后到,谁抢到是谁的。

CAS

CAS: 全称Compare and swap,字面意思:”比较并交换“。先来看一下CAS的伪代码:
在这里插入图片描述
address指的是内存地址,expectValue指的是期望值,swapValue指的是要交换的值。用期望值与内存中的值比较,如果相等,那么用swapValue覆盖内存中的值。如果期望值与内存中的值不等,什么也不做

CAS实现原子类

JDK中提供了原子类比如:AtomicInteger。对比之前写过的两个线程个自增五万次的操作,可以发现不使用synchronized而使用CAS就可以得到预期结果,从硬件层面CAS就支持原子性,这是纯用户态的操作

public class Demo01_CAS {
    public static void main(String[] args) throws InterruptedException {
        //原子整型
        AtomicInteger atomicInteger= new AtomicInteger();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                //自增
                atomicInteger.getAndIncrement();
            }
        });
        t1.start();

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                //获取并自增
                atomicInteger.getAndIncrement();
            }
        });
        t2.start();

        //等待两个线程执行完成
        t1.join();
        t2.join();
        //打印结果
        System.out.println(atomicInteger.get());
    }
}

如果要对原子类中的value做自增,那么就要先把当前的value值取出来,就可以把当前value值当作后续CAS的操作。两个线程通过CAS同时对一个共享变量做自增,通过不停的自旋(while循环)检查预期值来保证线程安全。 while循环是在应用层执行的,也就是用户态,所以比内核态的锁效率要高很多。
在这里插入图片描述

🟢执行过程:
线程1将value从主内存加载到自己的工作内存中,然后线程2也将value加载到自己的工作内存中。线程1对value做ADD,然后线程2对value做ADD。此时线程1执行比较并交换指令(cmpxchg),比较自己工作内存中的预期值value=0和主内存中的value=0比较,此时相等,将新值value=1给到主内存,此时主内存中value=1;t2线程比较预期值value=0与主内存中的value=1,此时不等,将主内存中的value=1加载到t2的工作内存中作为预期值,ADD之后工作内存中value=2,此时t2的工作内存中的预期值value=1与主内存的value=1相等,比较并交换,将主内存中的value变为2

在这里插入图片描述

CAS实现自旋锁

先来看一下自旋锁的伪代码:
在这里插入图片描述

CAS的ABA问题

有这样一个场景:当我把茶泡好之后就出门了,过十分钟我回来。
茶水的状态有两种情况:满的和不满。在满的情况下有两种可能:确实是没人喝过;别人喝了之后又给我添满了。虽然此时水的状态是满的,但这杯水已经不是我走的时候的那杯水了。这就是CSA中的ABA问题。A B A表示预期值的三个状态。在真实业务中,可能会造成影响。

解决ABA问题:

给预期值加一个版本号。在做CAS操作时同时更新预期值的版本号,版本号只增不减

CAS总结
1.先获取预期值;
2.通过CAS指定完成比较并交换;
3.如果在CAS的过程中预期值与真实值不相等,就进入自旋;
4.ABA问题,主要给预期值加一个版本号,在比较的时候同时比较真实值和版本号。

继续加油~

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值