2.CAS与锁相关

CAS与锁相关

  • 1. Java锁的分类
  • 2.悲观锁与乐观锁
    • 什么是悲观锁
    • 乐观锁
    • Mysql层面实现乐观锁
  • 3.公平锁与非公平锁
    • 公平锁
    • 非公平锁
    • 公平锁的底层是如何实现的
  • 4.锁的可重入性与自旋锁
    • 什么是锁的可重入性
    • 什么是CAS
    • CAS的运行逻辑
    • CAS的优缺点
    • ABA问题

1. Java锁的分类

(1)悲观锁与乐观锁
(2)公平锁与非公平锁
(3)自旋锁与重入锁
(4)重量级锁与轻量级锁
(5)独占锁与共享锁

2.悲观锁与乐观锁

什么是悲观锁

在Mysql数据库修改一行数据就是悲观锁,悲观锁就是比较悲观,当多个线程对同一行数据进行修改的时候,最后只有一个线程能够修改成功,只要谁能够获取到行锁,则其他线程都不能对该数据进行修改,且是阻塞状态。
在Java角度,悲观锁如果没有获取到锁,则会阻塞等待,唤醒锁的成本较高。
在java中,Lock 和 synchronize锁都是悲观锁。

乐观锁

乐观锁比较乐观,认为不会有其他线程修改数据,通过预期或者版本号比较,如果不一致的情况则通过循环控制修改,线程不会被阻塞,效率较高。但是会消耗CPU资源。

Mysql层面实现乐观锁

(1)在表字段中增加一个version字段
(2)多个线程对同一行数据实现修改操作时,提前获取当前的version作为更新的判断依据
(3)带着verison编号去执行更新操作,如果version变了则修改失败
(4)修改失败就不断重试,直到成功

3.公平锁与非公平锁

公平锁

就是比较公平,根据请求锁的顺序排队,先来请求的线程就先获取锁,后来的就后获取,采用队列进行存放, 类似于吃饭排队,打水排队。

公平锁示例:

    public static void main(String[] args) {
    	//通过 ReentrantLock 创建一个公平锁,根据参数传递 true
        ReentrantLock reentrantLock = new ReentrantLock(true);
        //创建10个线程测试这个 获取锁的过程
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    reentrantLock.lock();
                    System.out.println(Thread.currentThread().getName() + "--" + finalI);
                } catch (Exception e) {

                } finally {
                    if (reentrantLock != null) {
                        reentrantLock.unlock();
                    }
                }

            }).start();
        }
    }
}

运行结果:可以看到,获取锁的线程是有顺序的
2.CAS与锁相关_第1张图片

非公平锁

顾名思义,不是根据请求顺序排列的,通过争抢的方式获取锁,哪个线程进来都先看一眼锁在不在,在就获取,不在再去排队等着抢。

非公平锁的效率比公平锁的效率要高,synchronized是非公平锁

非公平锁的示例:只需要把上述代码改成 false

    public static void main(String[] args) {
        ReentrantLock reentrantLock = new ReentrantLock(true);
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    reentrantLock.lock();
                    System.out.println(Thread.currentThread().getName() + "--" + finalI);
                } catch (Exception e) {

                } finally {
                    if (reentrantLock != null) {
                        reentrantLock.unlock();
                    }
                }

            }).start();
        }
    }
}

运行结果:可以看到线程无序
2.CAS与锁相关_第2张图片

公平锁的底层是如何实现的

通过队列的方式去实现 线程获取锁的排队,根据顺序去获取锁并执行逻辑。

4.锁的可重入性与自旋锁

什么是锁的可重入性

在同一个线程中锁是可以不断传递的,可以直接获取。

比如递归、A调用B这种情况,不可能等A把锁释放了再调用B吧,因为B是A方法的一部分,A也没有运行完。所以锁是线程级别的,它会记录是哪个线程获取了这个锁,然后那个线程就可以直接用这个锁去对应的方法直接用。

例如 synchronized、lock、aqs。

什么是CAS

CAS:Compare And Swap ,比较并交换,执行函数CAS(V,E,N)

CAS不是Java实现的,是由JNI调用C++实现的。

通常来讲,锁的存在导致 没有获取到锁的线程 会阻塞,为了解决这个问题,CAS的存在让线程不再阻塞,而是靠着一直自旋(循环运行)。

CAS的运行逻辑

CAS有三个操作数 ,内存值V(共享值),旧的期望值E(读取到的共享值),要修改的新值N
当且仅当预期值与内存值相同时,将内存值V修改为N,否则什么都不做,这个概念其实与乐观锁的类似。
当 E==V时,才会改变V。

CAS的优缺点

优点:通过循环的方式解决了 线程因为锁阻塞的问题
缺点:循环次数过高的话,会导致CPU标高,所以需要控制次数。

ABA问题

通过以上可以知道, 比较V和E时,我们如何知道 即使V值 和E 相同,但是已经变过了呢??

一般可以增加一个版本号version

你可能感兴趣的:(JUC,java)