整体介绍
ReentrantLock是JUC自带的一种可重入锁
主要有两种锁形式
- 公平锁
- 非公平锁
主要功能有两种
- 加锁
- 释放锁
本质是AQS(同步等待队列),一个带头结点和尾结点的双向链表
当当前节点完成任务,会唤醒队列里面的下一个可唤醒节点
新的任务到来会放到队列的最后面,等待唤醒
AQS介绍
属性
AQS同步等待队列是一个带头尾节点地址的双向链表
重要几个属性
- state
- Node
- head
- tail
- exclusiveOwnerThread
state
/**
* 同步状态变量
* 用于判断锁是否已经被线程持有
* 当 state >= 1 表明前锁已经被线程持有
* 如果是可重入锁每次重新进入state+1
*/
private volatile int state;
Node
AQS同步队列存的就是Node节点
Node {
// 在同步队列中等待的线程等待超时或者被中断,取消继续等待
static final int CANCELLED = 1;
// 当前结点表示的线程在释放锁后需要唤醒后续节点的线程
static final int SIGNAL = -1;
//等待状态
//0-初始状态,激活状态
//1-CANCELLED 取消
//-1-SIGNAL 等待激活
volatile int waitStatus;
//前一个节点的地址
volatile Node prev;
//后一个节点的地址
volatile Node next;
//当前节点代表的线程
volatile Thread thread;
}
head
//AQS队列的头结点
private transient volatile Node head;
tail
//AQS队列的尾结点
private transient volatile Node tail;
exclusiveOwnerThread
//当前持有该锁的线程
private transient Thread exclusiveOwnerThread;
ReentrantLock介绍
主要是讲非公平锁,因为用的最多,后面有时间会补充公平锁
ReentranLock通过构造器设置公平锁和非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
公平锁和非公平锁的类结构图
非公平锁
非公平锁有两个地方提现
- 新节点在Lock()时会先尝试获取锁
- 第一次尝试获取锁失败时,会加入到等待队列,这时候会判断是不是前一个节点是不是head节点,如果是的话,会再次尝试获取锁
加锁
1、调用AQS里面通用加锁逻辑
public void lock() {
sync.acquire(1);
}
2、尝试获取锁或者加入到等待队列,否则中断当前线程
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node