阅读 73

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

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐