一步步教你Debug ReentrantLock源码

前言

ReentrantLock主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁,两者的实现类似。

CAS:Compare and Swap,比较并交换。CAS有3个操作数:内存值V、预期值A、要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。该操作是一个原子操作,被广泛的应用在Java的底层实现中。在Java中,CAS主要是由sun.misc.Unsafe这个类通过JNI调用CPU底层指令实现

AbstractQueuedSynchronizer简称AQS

AQS中最基本的数据结构——Node 属性值的含义

方法和属性值含义
waitStatus当前节点在队列中的状态
thread表示处于该节点的线程
prev前驱指针
predecessor返回前驱节点,没有的话抛出npe
nextWaiter指向下一个处于CONDITION状态的节点(由于本篇文章不讲述Condition Queue队列,这个指针不多介绍)
next后继指针

waitStatus有下面几个枚举值:

枚举含义
0当一个Node被初始化的时候的默认值
CANCELLED为1,表示线程获取锁的请求已经取消了
CONDITION为-2,表示节点在等待队列中,节点线程等待唤醒
PROPAGATE为-3,当前线程处在SHARED情况下,该字段才会使用
SIGNAL为-1,表示线程已经准备好了,就等资源释放了

代码展示

public class MyReetrantlock {

    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();

        new Thread(()->{
            lock.lock();
            try {
                TimeUnit.SECONDS.sleep(50);
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        },"A").start();


        new Thread(()->{
            try {
                lock.lock();
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        },"B").start();

    }
}

先在线程A的lock方法加端点

在这里插入图片描述

进入端点,到ReentrantLock的内部类 NonfairSync中

在这里插入图片描述

CAS设置 state从零更新成一,如设置成功返回true

在这里插入图片描述

设置当前线程A为独占线程

总结

由于线程抢占到了锁,把线程A设置为独占线程 state从0设置为1

在线程B的lock方法加入端点

在这里插入图片描述

进入到NonfairSync类的lock方法中

在这里插入图片描述

由于 state 已经被设置为1,compareAndSetState 方法返回false

在这里插入图片描述

调用了AQS(AbstractQueuedSynchronizer)类的acquire方法

在这里插入图片描述

进入到tryAcquire方法 首先会尝试获取锁,如是同一个线程,这就体现了ReentrantLock的可重入性, state会进行++操作

在这里插入图片描述

尝试获取锁失败,进入到addWaiter方法中

在这里插入图片描述

由于(CLH)队列中没有线程,pred=null,会调用enq方法

在这里插入图片描述

进入enq方法后,会不断的自旋

在这里插入图片描述

由于 尾节点为null,会创建一个哨兵节点

在这里插入图片描述

第二次循环 尾节点不为null 把线程B加入到队列的尾部

在这里插入图片描述

enq方法和addWaiter方法执行完,进入到acquireQueued方法中

在这里插入图片描述

不断自旋,拿到线程B的前驱节点p p为空节点,判断节点p是否为头节点,并且尝试获取锁

在这里插入图片描述

获取锁失败,判断是否停止自旋,进入shouldParkAfterFailedAcquire方法

在这里插入图片描述

首先获取到前驱节点的status锁的状态

在这里插入图片描述

前驱节点的status 为0,会将status从0设置-1,返回false

在这里插入图片描述

再次进入shouldParkAfterFailedAcquire方法

在这里插入图片描述

这时前驱节点已经为-1 返回true,执行parkAndCheckInterrupt方法,进行阻塞

在这里插入图片描述

这时线程B已经被阻塞,执行线程A的unlock方法

在这里插入图片描述

执行release方法唤醒线程

执行tryRelease方法 ,首先state减去要释放锁的数量,然后判断当前执行唤醒操作线程是否是独占线程,如不是抛出IllegalMonitorStateException异常,判断 锁是否已经被全部被释放,如state不为0,不能释放锁.如state为0,设置独占线程为null,返回true

在这里插入图片描述

执行unparkSuccessor方式唤醒线程B

在这里插入图片描述

总结

ReentrantLock方法的执行流程图如下
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值