一,AQS源码博文:并发编程:AbstractQueuedSynchronizer源码分析
二,ReentrantLock重入锁基本介绍
1,类图
* 从图中可以看到,ReentrantLock是顶层锁接口Lock的实现类,并实现了Lock所定义的关于锁操作的基本API
* ReentrantLock内部定义了三个有继承关系的内部类,Sync,FairSync,NonfairSync
* 其中Sync继承自AQS类,是ReentrantLock底层通过AQS进行线程调度的基础和扩展
* FairSync和NonfairSync分别继承自Sync类,是ReentrantLock对于公平锁和非公平锁的不同实现
2,重入锁
a,重入锁,表示重新进入的锁。也就是如果线程1通过调用lock方法获取了锁之后,再次调用lock,是不会阻塞等待获取锁的(如果等待,直接死锁),只需要增加锁的重入次数(即state)就可以(getHoldCount())。同时,在释放锁时,也不是直接释放掉当前线程的锁,而是对重入次数(即state)递减。当state为0时,再进行锁释放并唤醒下一个队列节点!如下例子
package com.gupao.concurrent;
import java.util.concurrent.locks.ReentrantLock;
/**
* 独占锁演示
* @author pj_zhang
* @create 2019-10-01 14:28
**/
public class ExclusiveTest {
// 定义全局重入锁对象
private static final ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
// 遍历并启动100道线程
Thread thread = new Thread(() -> {
try {
// 线程内部加锁并沉睡业务秒数,模拟获取到锁后不立即释放
// 多次加锁,模拟重入锁
reentrantLock.lock();
reentrantLock.lock();
reentrantLock.lock();
Thread.sleep(3000);
System.out.println(
Thread.currentThread().getName() + "获取锁,执行时间: "
+ System.currentTimeMillis());
// 通过getHoldCount()获取重入次数
System.out.println("当前重入次数:" + reentrantLock.getHoldCount());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// finally块中释放锁
// 此处只释放一次,模拟线程依旧阻塞,则不会唤醒阻塞线程,整个流程在此阻塞
reentrantLock.unlock();
System.out.println("释放一次后重入次数:" + reentrantLock.getHoldCount());
}
}, "THREAD_" + i);
thread.start();
System.out.println(thread.getName() + "启动成功...");
}
}
}
3,常用API
* 构造器
// 默认初始化非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 通过参数指定,构造公平锁或者非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
* 常用方法
// 加锁
void lock();
// 加锁,中断后抛异常
void lockInterruptibly() throws InterruptedException;
// 尝试加锁
boolean tryLock();
// 尝试加锁_带时间
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 解锁
void unlock();
// 获取重入次数
int getHoldCount();
...
三,非公平锁源码解析
1,构造器
// 默认构造器
public ReentrantLock() {
sync = new NonfairSync();
}
// 获取有参构造传递false
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2,尝试加锁源码解析
2.1,尝试加锁_不带时间
* tryLock():尝试加锁
public boolean tryLock() {
// 调用Sync的方法,即父类方法,此处公平锁和非公平锁一致
return sync.nonfairTryAcquire(1);
}
* java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire(int acquires)
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取state,即重入次数
int c = getState();
if (c == 0) {
// state为0,说明锁空闲,通过CAS替换加锁
if (compareAndSetState(0, acquires)) {
// 替换成功后,将当前线程设置为独占线程
setExclusiveOwnerThread(current);
return true;
}
}
// 如果当前线程就是独占线程,表示重入
else if (current == getExclusiveOwnerThread()) {
// 重入次数加上acquires表示的次数
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 并替换state的值
setState(nextc);
return true;
}
// 以上如果ruturn说明获取锁成功
// 否则失败
return false;
}
2.2,尝试加锁_带时间
* tryLock(long timeout, TimeUnit unit):尝试带时间加锁
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
// 此处调用AQS.tryAcquireNanos尝试加锁,注意将时间转换为纳秒
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
* AQS.tryAcquireNanos() 已经在AQS博文中解析,继续跟踪代码,会发现 tryAcquire() 方法在子类中重新定义,此处只对 tryAcquire() 方法进行分析
* java.util.concurrent.locks.ReentrantLock.NonfairSync#tryAcquire(int acquires):可以发现 tryAcquire() 中依旧调用 nonfairTryAcquire() 方法尝试获取锁
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
3,加锁源码解析
* lock():加锁
public void lock() {
// 此处调用非公平锁的 lock() 方法
sync.lock();
}
* java.util.concurrent.locks.ReentrantLock.NonfairSync#lock():调用非公平锁的 lock() 方法
final void lock() {
// 替换 state 状态,为0表示没有线程加锁,成功替换为1说明当前线程加锁成功
if (compareAndSetState(0, 1))
// 将独占线程指向当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 调用AQS的 acquire() 方法进行加锁,参考AQS源码博文
// 加锁失败后会添加到AQS同步队列
acquire(1);
}
4,释放锁源码解析
* unlock()
public void unlock() {
// 调用AQS.release方法
sync.release(1);
}
* java.util.concurrent.locks.AbstractQueuedSynchronizer#release(int arg):AQS中释放锁,先通过业务代码尝试释放锁,释放成功后唤醒下一个节点
public final boolean release(int arg) {
// 调用Sync.tryRelease尝试释放锁
if (tryRelease(arg)) {
// 释放锁成功后,唤醒同步队列上的下一个节点
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
* java.util.concurrent.locks.ReentrantLock.Sync#tryRelease(int releases):重入锁自定义尝试释放逻辑
protected final boolean tryRelease(int releases) {
// 对state进行递减,获取递减后的state值
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果c为0,表示所有重入已经被全部释放
if (c == 0) {
free = true;
// 将独占线程为空,当前线程不在占有锁
setExclusiveOwnerThread(null);
}
// 对state赋递减后的值
setState(c);
return free;
}
四,公平锁源码解析
1,构造器
// 获取有参构造传递true
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2,尝试加锁源码解析
2.1,尝试加锁_不带时间
* 与公平锁一致,此处翻代码没有非公平锁实现,疑惑ING!!!
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
2.2,尝试加锁_带时间
* 之前步骤与公平锁一致,调用最终到 tryAcquire() 调用自定义方法
* java.util.concurrent.locks.ReentrantLock.FairSync#tryAcquire(int acquires):尝试获取锁,注意公平锁与非公平锁尝试获取锁的区别
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取当前状态
int c = getState();
if (c == 0) {
// hasQueuedPredecessors:判断是否存在等待的前置节点,存在则获取失败
// 这也是公平锁和非公平锁的最主要区别,默认排到队尾执行,非公平锁直接插队获取锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 占有线程为当前线程,锁重入
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
* java.util.concurrent.locks.AbstractQueuedSynchronizer#hasQueuedPredecessors():判断队列中是否存在等待元素
public final boolean hasQueuedPredecessors() {
// 尾结点
Node t = tail;
// 头节点
Node h = head;
Node s;
// 头节点不是尾结点,说明存在多个节点
// 头节点的next为空,说明头节点正在运行中
// 或者next节点线程和当前线程不一致,说明存在等待线程
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
3,加锁源码解析
* 公平锁的 lock() 与非公平锁的 lock() 相比,同样是在每次调用 tryAcquire() 时,调用不同的实现。不同的实现已经在上面解析
4,释放锁源码解析
* 公平锁与非公平锁一致