ReentrantLock 的实现原理简析

ReentrantLock基于AQS实现线程同步,其内部维护FIFO双向链表。锁的获取与释放涉及节点添加、状态变更。释放锁时唤醒后续节点,非公平锁尝试直接CAS获取。AQS.acquire方法涉及tryAcquire、addWaiter、acquireQueued等步骤,确保线程安全。
摘要由CSDN通过智能技术生成

ReentrantLock 的实现原理

在 Lock 中,用到了一个同步队列 AQS,全称AbstractQueuedSynchronizer,它是一个同步工具也是 Lock 用来实现线程同步的核心组件。

AQS 的功能分为两种:独占和共享
独占锁,每次只能有一个线程持有锁,ReentrantLock 就是以独占方式实现的互斥锁。
共 享 锁 , 允 许 多 个 线 程 同 时 获 取 锁 , 并 发 访 问 共 享 资 源 , 比 如ReentrantReadWriteLock。

AQS 队列内部维护的是一个 FIFO 的双向链表,这种结构的特点是每个数据结构都有两个指针,分别指向直接的后继节点和直接前驱节点。每个 Node 其实是封装当前线程,当线程争抢锁失败后会封装成 Node 加入到 ASQ 队列中去;当获取锁的线程释放锁以后,会从队列中唤醒一个阻塞的节点(线程)。

在这里插入图片描述

AQS释放锁以及添加线程对于队列的变化

添加节点

  1. 新的线程封装成 Node 节点追加到同步队列中,设置 prev 节点以及修改当前节
    点的前置节点的 next 节点指向自己
  2. 通过 CAS 讲 tail 重新指向新的尾部节点

释放锁

head 节点表示获取锁成功的节点,当头结点在释放同步状态时,会唤醒后继节点,如果后继节点获得锁成功,会把自己设置为头结点,节点的变化过程如下:
4. 修改 head 节点指向下一个获得锁的节点
5. 新的获得锁的节点,将 prev 的指针指向 null
设置 head 节点不需要用 CAS,原因是设置 head 节点是由获得锁的线程来完成的,而同步锁只能由一个线程获得,所以不需要 CAS 保证,只需要把 head 节点设置为原首节点的后继节点,并且断开原 head 节点的 next 引用即可。

源码

lock

public void lock() {
   
        sync.lock();
    }

ReentrantLock 中sync 实际上是一个抽象的静态内部类,它继承了 AQS 来实现重入锁的逻辑.

以非公平锁为例,来看看 lock 中的实现

  1. 非公平锁和公平锁最大的区别在于,在非公平锁中,不管有没有线程排队, cas 抢占一下
  2. CAS 成功,就表示成功获得了锁
  3. CAS 失败,调用 acquire(1)走锁竞争逻辑
final void lock() {
   
   if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

通过 cas 乐观锁的方式来做比较并替换,这段代码的意思是,如果当前内存中的state 的值和预期值 expect 相等,则替换为 update。更新成功返回 true,否则返回 false.这个操作是原子的,不会出现线程安全问题

/**
 * The synchronization state.
 */
 private volatile int state;

1.state=0表示无锁。
2.state>0表示获得锁的次数,重入的次数。

AQS .accquire

这个方法的主要逻辑是

  1. 通过 tryAcquire 尝试获取独占锁,如果
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值