• ReentrantLock和AQS


    一、ReentrantLock和AQS的关系

    因为java并发包下很多API都是基于AQS来实现的加锁和释放锁等功能的,AQS是java并发包的基础类。

    举个例子,比如说ReentrantLock、ReentrantReadWriteLock底层都是基于AQS来实现的。

    那么AQS的全称是什么呢? AbstractQueuedSynchronizer,抽象队列同步器。

    二、ReentrantLock加锁和释放锁的底层原理

    这个AQS对象就是ReentrantLock可以实现加锁和释放锁的关键性的核心组件。这个AQS对象内部有一个核心的变量叫做state,是int类型的,代表了加锁的状态。初始状态下,这个state的值是0。

    另外,这个AQS内部还有一个关键变量,用来记录当前加锁的是哪个线程,初始化状态下,这个变量是null。除此之外,AQS对象内部还有一个线程的等待队列,用于存放想要获取锁但是还没有获取锁的线程。

    现在如果有一个线程过来尝试用ReentrantLock的lock()方法进行加锁,会发生什么事情?线程跑过来调用ReentrantLock的lock()方法尝试进行加锁,这个加锁的过程,直接就是用CAS操作将state值从0变为1。

    如果之前没人加过锁,那么state的值肯定是0,此时线程1就可以加锁成功。一旦线程1加锁成功了之后,就可以设置当前加锁线程是自己。每次线程1可重入加锁一次,会判断一下当前加锁线程是否是自己,如果是那么他自己就可以可重入多次加锁,每次加锁就是把state的值给累加1

    ReentrantLock是一个可重入锁。可重入锁的意思,就是你可以对一个ReentrantLock对象多次执行lock()加锁和unlock()释放锁,也就是可以对一个锁加多次,叫做可重入加锁。

    我们来看看锁的互斥是如何实现的?

    接着,如果线程1加锁了之后,线程2跑过来加锁会怎么样呢?线程2发现state的值不是0,所以CAS操作将state从0变为1的过程会失败,因为state的值当前为1,说明已经有人加锁了!

    接着线程2会看一下,是不是自己之前加的锁啊?当然不是了,“加锁线程”这个变量明确记录了是线程1占用了这个锁,所以线程2此时就是加锁失败。

    接着,线程2会将自己放入AQS中的一个等待队列,因为自己尝试加锁失败了,此时就要将自己放入队列中来等待,等待线程1释放锁之后,自己就可以重新尝试加锁。

    释放锁

    接着,线程1在执行完自己的业务逻辑代码之后,就会释放锁!他释放锁的过程非常的简单,就是将AQS内的state变量的值递减1,如果state值为0,则彻底释放锁,会将“加锁线程”变量也设置为null!

    接下来,会从等待队列的队头唤醒线程2重新尝试加锁。

    参考:https://zhuanlan.zhihu.com/p/86072774
  • 相关阅读:
    linux 挂载本地iso
    安装python
    QT中如何使用MYSQL 以及静态编译QT中如何加上MYSQL(2)
    qt中如何使用mysql 以及静态编译qt中如何加上mysql(1)
    关于写qt项目时的代码格式问题
    qt相关学习以及 qt creator如何静态编译
    Qt Creator中如何在QLabel上画矩形框并且保存画完后的图形
    android localsocket 传输大量数据的问题
    android binder使用demo
    android binder使用
  • 原文地址:https://www.cnblogs.com/jingpeng77/p/13845269.html
Copyright © 2020-2023  润新知