在了解AQS后,那应该怎么了解AQS的最佳实践那,我想再也没有Java官方的实践更加优秀了,这次我打算重新拿出系统源代码,并将其总结成一系列文章,以供将来查看.
本次准备分六篇文章用来分析基于AQS实现的类
本篇文章为系列文章的第二篇,本篇文章介绍ReentrantLock(可重入锁)公平锁代码实现,ReentrantLock是一个可重入的互斥锁Lock,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
可重入锁指的是线程可以重复获取同一把锁.
首先,我们从总体过程入手,了解ReentrantLock公平锁的执行逻辑,然后逐步深入分析源代码。
基于上面提到的过程,让我们来看看源代码实现逻辑.首先,让我们看看如果创建公平锁。
//默认构造器 构建非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
//传值 构建 true 构建 公平锁,false 构建非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
由上面代码可知,ReentrantLock提供了两个构造方法,默认无参构造器构建非公平锁,我们通过有参构造器传入true创建公平锁
// 公平锁为一个内部类 ,他继承 Sync 内部类
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
//不做任何操作 直接使用AQS acquire方法
final void lock() {
acquire(1);
}
//重写 tryAcquire
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取当前状态
int c = getState();
//如果状态为0 则表示没有线程获取锁
if (c == 0) {
//与非公平锁不同的是,此处多了hasQueuedPredecessors()判断,该方法是实现公平锁的关键。
//如果hasQueuedPredecessors返回true,表示有其他线程先于当前线程等待获取锁,
//此时为了实现公平,保证等待时间最长的线程先获取到锁,不能执行CAS。
//CAS可能会破坏公平性。反之,如果hasQueuedPredecessors返回false,则可以执行CAS更新同步状态尝试获取锁。
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;
}
//如果以上都没有设置成功
//则返回失败 执行AQS排队机制
return false;
}
}
由上面代码可知,FairSync继承SYNC,公平锁和非公平锁最大的区别就是在设置同步状态的时候,增加了一个校验hasQueuedPredecessors,该方法保证不会存在插队现象。
//查询是否有线程等待获取的时间长于当前线程。
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
通过以上分析可以知道hasQueuedPredecessors返回true代表有别的线程在CLH队列中排了当前线程之前,返回false代表当前线程处于CLH队列的第一个线程。
tail 先赋值head后赋值保证了后续节点 h.next 不会为空
发生在t == null场景内, compareAndSetHead(new Node()) 刚为head赋值完成,tail 还没有被head赋值
发生在t == null场景内,compareAndSetHead(new Node()) 还没有为head赋值完成
发生在t == null场景内,tail 被head赋值完
该场景不会发生,tail 如果不为null则,则head必然不为nul
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
以上是ReentrantLock-公平锁的获取和释放源码分析,公平锁的实现也很简单,这些都因为它基于AQS实现,AQS已经帮我们实现了大多数功能,了解ReentrantLock源码实现能够让我们更加深入的了解AQS设计思想。