前言
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方法的执行流程图如下