Java并发之ReentrantLock
一、什么是AQS?
AQS是AbstractQueuedSynchronizer的简称,AQS提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架。AQS实现了等待队列、条件队列、独占或共享锁的获取。而在Java中也提供了许多基于AQS实现的锁:
1.1 AQS的特性
- 阻塞的等待队列
- 共享或独占
- 公平或非公平
- 可重入
- 允许中断
1.2 AQS内部信息
AQS中是基于一个volatile修饰的变量来标识共享资源的状态,以及提供对应的读写方法:
//用于标识共享资源的状态
private volatile int state;
//对state的读方法
protected final int getState() {
return state;
}
//对state的写方法
protected final void setState(int newState) {
state = newState;
}
//对state的CAS写方法
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
AQS中定义的两种队列:
同步等待队列:主要作用就是存放竞争锁失败的线程,这个队列是双端链表队列
条件等待队列:调用await()方法后会释放锁,同时线程会加入条件等待队列,并且当前线程会挂起;调用signal()会唤醒条件队列中挂起的线程,并转移到同步等待队列中。
同步等待队列数据结构:

条件等待队列数据结构:

AQS中定义的两种资源共享方式:
Exclusive-独占:资源只能由一个线程获取,例如ReentrantLock
Share-共享:资源可以由多个线程获取,例如Semaphore、CountDownLatch
AQS中Node的五个状态:
1.值为0的时候,表示是初始状态,当前节点正在同步等待队列中等待获取锁;
2.CANCELLED = 1,表示线程已取消;
3.SIGNAL = -1,表示当前节点的后续节点可以被唤醒;
4.CONDITION = -2,表示当前节点在条件等待队列中;
5.PROPAGATE = -3,表示后续的acquireShared能够得以执行。
1.3 AQS同步等待队列
同步等待队列也叫CLH队列,是一种基于双向链表实现的队列,是FIFO。
处理流程:
1.当线程获取锁失败后,AQS会构建一个Node节点,然后存放到CLH队列中,并阻塞当前队列;
2.当持有锁的线程释放锁后,AQS会唤醒头结点,让头结点对应的线程去获取锁;
3.如果持有锁的线程调用signal()、signalAll()方法,就会将条件队列中的节点转移到同步队列。
1.4 条件等待队列
AQS中的条件等待队列是单向链表数据结构,使用nextWatier进行连接。
1.调用await方法后会释放锁,并且会阻塞当前线程,同时向Condition队列尾部添加一个节点;
2.调用signal方法后会将Condition首部的节点移动到同步队列的尾部,然后唤醒因await方法阻塞的线程。
//await signal
private static void testReentrantLock() throws InterruptedException {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();
Thread thread1 = new Thread(() -> {
reentrantLock.tryLock();
try{
System.out.println("Thread1 get lock!");
condition.await();
System.out.println("唤醒后,再次执行");
}catch (Exception e){
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
});
Thread thread2 = new Thread(() -> {
reentrantLock.lock();
try{
System.out.println("Thread2 get lock!");
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
});
thread1.start();
Thread.sleep(1);
thread2.start();
System.out.println("main Thread!");
}
二、Java层面实现的ReentrantLock锁
ReentrantLock是基于AQS实现的,它的功能类似于synchronized,是一种互斥锁(独占锁),可以保证线程并发安全。
2.1 Reentrantlock的特点
1.支持线程中断
2.获取锁可以设置超时时间
3.支持公平和非公平锁
4.支持多个条件变量、条件队列
5.支持锁重入
2.2 synchronized和Reentrantlock的区别
1.synchronized是jvm层面提供的锁,Reentrantlock是Java层面提供的锁;
2.synchronized是非公平的锁,而Reentrantlock既有公平也有非公平实现;
3.synchronized不可以被中断,而Reentrantlock支持中断;
4.synchronized支持异常自动释放锁,而Reentrantlock则要在finally块手动释放
//Reentrantlock的使用
public static void main(String[] args) throws InterruptedException {
// ReentrantLock lock = new ReentrantLock(true); //公平锁
ReentrantLock lock = new ReentrantLock(); //非公平锁
for (int i = 0; i < 500; i++) {
new Thread(() -> {
//加锁
lock.lock();
try {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug(Thread.currentThread().getName() + " running...");
} finally {
//解锁
lock.unlock();
}
}, "t" + i).start();
}
}
三、源码分析
3.1 加锁逻辑
非公平加锁:
acquire方法:

tryAcquire方法最终实现:

addWaiter方法:

enq:

acquireQueued:

3.2 释放锁
release方法:
tryRelease:

unparkSuccessor:

