java多线程之ReentrantLock公平锁源码解析
前言
本篇是基于JDK8版本,分析ReentrantLock公平锁的获取和释放的源码。
1、获取公平锁
lock()方法在ReentrantLock.java的FairSync类中实现,它的源码如下:
final void lock() { //ReentrantLock是可重入锁,所以单个线程每次进入都+1,初始值是0 acquire(1); } 复制代码
acquire()在AbstractQueuedSynchronizer(AQS)抽象类中定义的,它的源码如下:
public final void acquire(int arg) { //这里总共有4个方法,tryAcquire实现方法在FairSync类中 //addWaiter、acquireQueued、selfInterrupt三个方法在AQS中实现 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } 复制代码
1.1、tryAcquire方法
tryAcquire方法作用就是尝试去获取锁,获取成功返回true,获取失败则返回false。
protected final boolean tryAcquire(int acquires) { //获取当前线程 final Thread current = Thread.currentThread(); //获取锁状态,就是每次线程进入都要加1的那个值 int c = getState(); //c等于0,就是初始状态,锁没有被占用 if (c == 0) { //判断队列中当前线程是否是第一个 //CAS再次判断锁是否没有被占用 //设置锁被当前线程占用,然后返回 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { //如果锁的拥有者是当前线程 //锁的状态+1 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); //更新锁的状态,然后返回 setState(nextc); return true; } return false; } 复制代码
1.2、addWaiter方法
addWaiter方法是创建当前线程的Node节点,并在Node中记录锁是“独占锁”类型,并且将该节点添加到队列的末尾,当前线程就处于等待状态了,我们先看看Node节点的源码:
private transient volatile Node head; // CLH队列的队首 private transient volatile Node tail; // CLH队列的队尾 // 队列的Node节点 static final class Node { static final Node SHARED = new Node(); static final Node EXCLUSIVE = null; // 线程已被取消,对应的waitStatus的值 static final int CANCELLED = 1; // 当前线程的后一个线程需要被唤醒,对应的waitStatus值 static final int SIGNAL = -1; // 线程休眠,对应的waitStatus的值 static final int CONDITION = -2; // 其它线程获取到“共享锁”,对应的waitStatus的值 static final int PROPAGATE = -3; volatile int waitStatus; // 前一节点 volatile Node prev; // 后一节点 volatile Node next; // 节点所对应的线程 volatile Thread thread; // 标记队列是独占锁还是共享锁 Node nextWaiter; //共享锁则返回true,独占锁则返回false。 final boolean isShared() { return nextWaiter == SHARED; } // 返回前一节点 final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } } 复制代码
看完Node源码,下面我们再看看addWaiter方法
private Node addWaiter(Node mode) { //创建当前线程Node节点 Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; //判断队列是否为空,不为空则添加到队列末尾 if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } //为空,则新建一个队列,并添加进去 enq(node); return node; } 复制代码
1.3、acquireQueued方法
acquireQueued方法是在队列中获取锁
final boolean acquireQueued(final Node node, int arg) { //标记acquire是否成功 boolean failed = true; try { //标记是否被挂起过,后面是否要执行中断方法 boolean interrupted = false; //死循环,正常情况下线程只有获得锁才能跳出循环 for (;;) { final Node p = node.predecessor(); //当前节点是头节点,并且获取锁成功,才能退出循环,这才体现出公平锁的“公平” if (p == head && tryAcquire(arg)) { //设置头节点 setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())//判断是否要阻塞当前线程,如果要阻塞挂起,则interrupted为true,表示曾经中断过 interrupted = true; } } finally { if (failed) //如果tryAcquire出现异常那么取消当前结点的获取 cancelAcquire(node); } } 复制代码
1.4、selfInterrupt方法
这个方法很简单,就是中断当前线程
static void selfInterrupt() { Thread.currentThread().interrupt(); } 复制代码
2、释放公平锁
public void unlock() { //这里的1和获取锁的1是相同的,释放锁的时候是-1 sync.release(1); } 复制代码
2.1、release方法
public final boolean release(int arg) { //tryRelease来尝试释放当前线程持有的锁。 //成功的话,则唤醒后继等待线程,并返回true。否则,直接返回false if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) //唤醒当前线程的后继线程 unparkSuccessor(h); return true; } return false; } 复制代码
2.2、unparkSuccessor方法
private void unparkSuccessor(Node node) { //获取当前线程的状态 int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); //循环获取当前节点的后继节点状态小于等于0的节点 Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) //唤醒后继对应节点 LockSupport.unpark(s.thread); } 复制代码
结束语
本篇详细的分析了ReentrantLock获取公平锁和释放公平锁的代码,大部分的代码上面都进行了中文注释。下一篇将介绍ReentrantLock的非公平锁的获取和释放。
分析源码不易,如果有帮助到你请随手点个赞,谢谢大家!
作者:夏夜凉月
链接:https://juejin.cn/post/7023543515338506270