ReentrantLock源码解析

1、AQS

AQS本质上就是java中的AbstractQueuedSynchronizer

AQS的结构为双向队列

    static final class Node {...}

    private transient volatile Node head;

    private transient volatile Node tail;

Node中的几个重要的属性:

static final class Node {
      // 排他锁的标识
      static final Node EXCLUSIVE = null;
      // 如果带有这个标识,说明是失效了
      static final int CANCELLED =  1;
      // 如果带有这个标识,有效节点,说明后继节点需要被唤醒
      static final int SIGNAL    = -1;      
      static final int CONDITION = -2;     
      static final int PROPAGATE = -3;
      // Node存储标识的地方
      volatile int waitStatus;
      // 指向上一个节点
      volatile Node prev;
      // 指向下一个节点
      volatile Node next; 
      // 当前Node绑定的线程
      volatile Thread thread;    
      // 返回前驱节点,如果前驱节点为空,抛出异常
      final Node predecessor() throws NullPointerException {
          Node p = prev;
          if (p == null)
              throw new NullPointerException();
          else
              return p;
      }
	  // 表示同步状态;如果可以从0变为1,说明拿到了锁资源,
	  // 如果为1, 2, 3说明其他线程占用了锁资源
	  private volatile int state;
 }

2、加锁

  1. ReentranLock既可以实现公平锁,也可以实现非公平锁,也是互斥锁,以及可重入锁

在这里插入图片描述

public void lock() {
   // sync分为公平锁和非公平锁
    sync.lock();
 }
  1. 以非公平锁为例
static final class NonfairSync extends Sync {
     final void lock() {
         // 通过CAS的方式尝试将state从0修改为1,如果返回为true,代表修改成功;
         // 如果修改失败,返回false
         if (compareAndSetState(0, 1))
         		// 将一个属性设置为当前线程,这个属性是AQS的父类提供的,表示当前前程拿到了锁资源
             setExclusiveOwnerThread(Thread.currentThread());
         else
             acquire(1);
     }
}
  1. acquire方法
public final void acquire(int arg) {
		// tryAcquire再次尝试获取锁资源,如果尝试成功,返回true
    if (!tryAcquire(arg) && 
       // 获取锁资源失败后,将当前线程封装成一个Node追加到AQS的队列中
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        // 线程中断
        selfInterrupt();
}
  1. tryAcquire方法:CTRL+T(!tryAcquire(arg) : 返回true的方法有两种,一个是拿到了锁资源,另一个是重入成功)如果返回了false,进行非操作后,就需要走acquireQueued方法
protected final boolean tryAcquire(int acquires) {
	return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
		// 获取当前线程
    final Thread current = Thread.currentThread();
    // 获取AQS的state的值
    int c = getState();
    // 如果为0,代表刚才使用锁资源的已经将锁资源释放,可以尝试竞争锁资源
    if (c == 0) {
    		// CAS尝试修改state,从0到1,如果返回为true,代表修改成功;
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 如果当前占有锁资源的线程是否是当前线程,
    else if (current == getExclusiveOwnerThread()) {
    	 // 将c加一
        int nextc = c + acquires;
        // 如果加一之后小于0,说明超过最大锁数
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        // 重新对state赋值
        setState(nextc);
        // 锁重入成功
        return true;
    }
    return false;
}
  1. addWaiter(Node.EXCLUSIVE)
// 说明获取锁资源失败
private Node addWaiter(Node mode) {
 	// 创建Node类,并且设置thread为当前线程,mode表示Node.EXCLUSIVE排他锁的标识
   Node node = new Node(Thread.currentThread(), mode);
   // Try the fast path of enq; backup to full enq on failure
   // 获取AQS中的尾部节点
   Node pred = tail;
   // 如果队列中有节点
   if (pred != null) {
   		// node的前驱为尾部节点,所以node为tail节点
       node.prev = pred;
       // 使用CAS的方式,将当前tail节点转换为当前node节点
       if (compareAndSetTail(pred, node)) {
          // 之前尾结点的next为当前节点,并返回尾部节点
           pred.next = node;
           return node;
       }
   }
   // 队列中无节点的方式,或前面CAS操作失败,也会进入:表示往队列尾部添加节点
   enq(node);
   return node;
}
private Node enq(final Node node) {
		// 死循环,直到初始化成功
    for (;;) {
    	 // 获取tail节点
        Node t = tail;
        if (t == null) { // Must initialize
        		 // 没有节点
            if (compareAndSetHead(new Node())) // 初始化一个node作为head,没有意义
            		// 将头和尾都指向了这个节点(虚拟头结点) 第一次循环结束
                tail = head;
        } else {
            node.prev = t;
            // 有节点
            // 使用CAS的方式,将当前tail节点转换为当前node节点
            if (compareAndSetTail(t, node)) {
            		 // 下一个节点next为node
                t.next = node;
                return t;
            }
        }
    }
}
  1. acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 已将node加入到双向队列中,执行当前方法
final boolean acquireQueued(final Node node, int arg) {
	// 获取锁资源的标识
    boolean failed = true;
    try {
    	// 标识
        boolean interrupted = false;
        for (;;) { // 死循环
        			// 获取当前节点的上一个节点,如果没有节点,就会抛出异常
            final Node p = node.predecessor();
            // 如果p(上一个节点)是头,就再次获取锁资源tryAcquire
            if (p == head && tryAcquire(arg)) {
            		// 已经拿到锁资源了, 设置当前节点为头结点,将Thread和prev设置为null
                setHead(node);
                p.next = null; // help GC
                failed = false; // 将标识修改为false,会影响到下面finally中的执行,如果为true
                return interrupted; // 返回标识
            }
            // 保证上一个节点的状态是-1,才会返回true,会将线程阻塞,等待唤醒,获取锁资源。才会走下面的parkAndCheckInterrupt
            if (shouldParkAfterFailedAcquire(p, node) &&
            // 基于unsafe类的park方法挂起线程
                parkAndCheckInterrupt()) // 这里是唯一可能出现的异常会抛出 jvm内部出现问题时,finally代码块的内容执行几率为0
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

setHead方法

// sethead里面
private void setHead(Node node) {
   head = node;
    node.thread = null;
    node.prev = null;
}

shouldParkAfterFailedAcquire方法

// 上文中调用时使用的是p, node表示的是前驱节点和当前节点
// 所以下文的pred是前驱节点,node是当前节点
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 前驱节点的状态
    int ws = pred.waitStatus;
    // 如果具有这个标识,说明后继节点需要被唤醒
    if (ws == Node.SIGNAL)
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true;
    // 如果上一个节点已经失效了,
    if (ws > 0) {
        /*
         * Predecessor was cancelled. Skip over predecessors and
         * indicate retry.
         */
        do {
        		// 将当前节点的前驱节点指向前驱节点的前驱
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0); // 一直找找到状态小于0的节点(-1)
        // 前驱节点的前驱的后继为当前节点
        pred.next = node;
    } else {
        /*
         * waitStatus must be 0 or PROPAGATE.  Indicate that we
         * need a signal, but don't park yet.  Caller will need to
         * retry to make sure it cannot acquire before parking.
         */
         // 小于等于0  但是不等于-1的情况,将上一个有效节点的状态修改为-1
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

compareAndSetWaitStatus方法

 private static final boolean compareAndSetWaitStatus(Node node,
                                                  int expect,
                                                     int update) {
    return unsafe.compareAndSwapInt(node, waitStatusOffset,
                                    expect, update);
}

parkAndCheckInterrupt方法

private final boolean parkAndCheckInterrupt() {
   LockSupport.park(this);
    return Thread.interrupted();
}

cancelAcquire方法

private void cancelAcquire(Node node) {
// 当前节点为null 结束 健壮性判断
    // Ignore if node doesn't exist
    if (node == null)
        return;
// node的线程为空,竞争锁资源无关,线程也就没有用了
    node.thread = null;
    Node pred = node.prev;
    // 如果前驱节点的状态大于0,说明为失效节点,知道找到一个有效节点
    while (pred.waitStatus > 0)
        node.prev = pred = pred.prev;
    // 将第一个不是失效节点的后继节点声明出来
    Node predNext = pred.next;

    // 当前节点置为失效节点
    node.waitStatus = Node.CANCELLED;
    // 如果当前节点为尾结点,将尾结点设置为前驱节点
    if (node == tail && compareAndSetTail(node, pred)) {
    		// 用CAS的方式,将尾结点的next设置为null
        compareAndSetNext(pred, predNext, null);
    } else {
        // 中间节点操作
        int ws;
        // 如果上一个节点不是头结点,
        if (pred != head &&
        // 上一个节点的状态,是不是有效
            ((ws = pred.waitStatus) == Node.SIGNAL ||
            // pred需要唤醒后继节点,
             (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
            pred.thread != null) {
            Node next = node.next;
            if (next != null && next.waitStatus <= 0)
                compareAndSetNext(pred, predNext, next); // 尝试将pred的next指向当前节点的next(有效的next的节点)
        } else {
        // 头部节点的操作
            unparkSuccessor(node);
        }

        node.next = node; // help GC
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值