ReentrantLock源码探究1:非公平锁的获取和释放 (2)

线程1调用lock方法

final void lock() { if (compareAndSetState(0, 1)) //使用CAS将同步状态设置为1 setExclusiveOwnerThread(Thread.currentThread());//成功则设置线程1为当前锁的独占线程 else acquire(1); //设置失败时,尝试获取锁 } //上述代码正常情况下执行完毕后,线程1成为了独占线程。

线程2此时也尝试获取锁,调用lock方法,此时CAS设置时会失败,进入acquire方法。

public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); //重新获取锁失败且线程发生了中断,自行中断 }

这里面,会首先调用tryAcquire方法尝试再次获取锁,因为我们演示的是非公平锁,因此调用的方法是nonfairTryAcquire。

final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); //current指向当前线程2 int c = getState(); //若线程1未释放锁,则c>0,若线程1已经释放锁,则c=0 if (c == 0) { //线程1已经释放了锁 if (compareAndSetState(0, acquires)) { //使用CAS将state设置为1 setExclusiveOwnerThread(current); //并设置线程2为独占线程 return true; //返回true,获取锁成功 } } //判断该线程是否是重入,即之前已经获取到了锁 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; //每重入一次,将state+1。 if (nextc < 0) // overflow //state+1<0,说明原state为负数,抛出异常 throw new Error("Maximum lock count exceeded"); setState(nextc); //设置state为新值 return true; //返回true,获取重入锁成功。 } return false; //返回flase,获取锁失败 }

此时线程2使用tryAcquire方法获取锁,如果也是失败,那么,会调用addWaiter(Node.EXCLUSIVE)方法

private Node addWaiter(Node mode) { //此处mode为独占模式 Node node = new Node(Thread.currentThread(), mode);//将当前线程(此处为线程2)绑定到新节点node上,并设置为独占模式 // Try the fast path of enq; backup to full enq on failure Node pred = tail; //获取原队列的尾节点pred if (pred != null) { //若原尾节点pred非空,则说明已经存在一个队列 node.prev = pred; //设置新节点node的前置为pred if (compareAndSetTail(pred, node)) {//使用CAS设置新的尾节点为node pred.next = node; //设置pred的后置为node,建立双向链接 return node; //返回node } } enq(node); //进入此处说明原队列不存在,需要初始化队列 return node; } private Node enq(final Node node) { //此处传入的参数node是绑定了线程2的节点 for (;;) { Node t = tail; //获取原队列的尾节点t if (t == null) { // Must initialize //若尾节点为空,说明队列尚未形成 if (compareAndSetHead(new Node())) //设置一个空的,未绑定任何线程的节点为新队列的头节点 tail = head; //新队列只有一个节点,既是头也是尾 } else { //若t非空,说明队列已经形成 node.prev = t; //将node的前置设为t if (compareAndSetTail(t, node)) { //CAS设置新的尾节点为node t.next = node; //设置t的后继为node,建立双向链接 return t; //返回t } } } }

现在看外层方法acquireQueued,此时传入的参数node是线程2所在节点,该方法的作用是在等待队列中,当有其他线程释放了资源,那么队列中在等待的线程就可以开始行动

final boolean acquireQueued(final Node node, int arg) { boolean failed = true; //是否获取到资源 try { boolean interrupted = false; //等待过程中是否被中断 //自旋,维护等待队列中线程的执行。 for (;;) { final Node p = node.predecessor(); //获取node的前置p if (p == head && tryAcquire(arg)) { //若前置p为头结点并且重新获取锁成功 setHead(node); //设置新的头节点为node p.next = null; // help GC //取消p和链表的链接 failed = false; //获取资源未失败 return interrupted; //等待过程未被中断 } if (shouldParkAfterFailedAcquire(p, node) && //若前置节点是Node.SIGNAL状态 parkAndCheckInterrupt()) //将节点设置为Waitting状态 interrupted = true; //此时线程中断状态为true } } finally { if (failed) //如果获取资源成功那么取消获取过程 cancelAcquire(node); } } private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; //获取前置节点的等待状态 if (ws == Node.SIGNAL) //Node.SIGNAL表示继任者线程需要被唤醒,那么就可以直接返回; /* * This node has already set status asking a release * to signal it, so it can safely park. */ return true; if (ws > 0) { //若ws>0,说明前驱被取消,那么执行循环往前一直查找,知道找到未被取消的,将node排在它的后面。 /* * Predecessor was cancelled. Skip over predecessors and * indicate retry. */ do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { //进入else,说明ws=0或者Node.PROPAGATE /* * 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. */ //使用CAS设置前置的节点状态为Node.SIGNAL compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyfwsp.html