ReentrantLock解析及源码分析 (4)

这个方法的设计上如果自身是头节点的后继节点,那么有可能头节点会很快处理完成任务释放锁,自己就可以获取到锁,避免进行线程阻塞、唤醒操作,减少资源消耗

/** * Acquires in exclusive uninterruptible mode for thread already in * queue. Used by condition wait methods as well as acquire. * * @param node the node * @param arg the acquire argument * @return {@code true} if interrupted while waiting */ final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { //当前线程中断的标志位 boolean interrupted = false; for (;;) { //获取当前节点的前驱节点 final Node p = node.predecessor(); //前驱节点是头节点则尝试调用同步器的tryAcquire获取锁 //第一次进入者方法时尝试一次获取锁,获取失败则会进行判断是否需要进行阻塞 //如果需要阻塞则阻塞,如果不需要阻塞则会将前驱节点的waitStatus设置为SIGNAL, //再次循环获取锁失败,再次进入判断是否需要阻塞时一定会被阻塞 //获取失败即便先驱节点是头节点也会被阻塞 if (p == head && tryAcquire(arg)) { //成功获取锁则将当前节点设置为头节点 setHead(node); //原先区节点的下一个节点置空, p.next = null; // help GC //怎么help个人理解当当前节点称为头节点再被释放的时候,那么当前节点可以做到不可达,从而gc failed = false; //返回线程在队列中等待期间是否被中断 return interrupted; } //在先驱节点不是头结点的情况下阻塞当前线程并使其睡眠 //直到被其他线程唤醒,这样可以减少CPU的空转,提高效率 //从阻塞唤醒后继续for循环直到获取到锁, if (shouldParkAfterFailedAcquire(p, node) && //阻塞当前线程直到有其他线程唤醒,并返回中断信息 parkAndCheckInterrupt()) //如果在线程阻塞休眠期间线程被中断则设置终端标记位, interrupted = true; } } finally { //如果没有获取到锁(获取锁的过程出了意外),或者取消了获取锁,则取消当前线程获取锁的操作 if (failed) cancelAcquire(node); } }

shouldParkAfterFailedAcquire()方法,在AQS中实现

/** * Checks and updates status for a node that failed to acquire. * Returns true if thread should block. This is the main signal * control in all acquire loops. Requires that pred == node.prev. * * @param pred node's predecessor holding status * @param node the node * @return {@code true} if thread should block */ private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //获取先驱节点的等待状态 int ws = pred.waitStatus; //SIGNAL前驱节点准备好唤醒后继节点,后继节点可以安全的阻塞 if (ws == Node.SIGNAL) /* * This node has already set status asking a release * to signal it, so it can safely park. */ return true; //大于0表示先驱节点已经被取消获取锁及排队 if (ws > 0) { /* * Predecessor was cancelled. Skip over predecessors and * indicate retry. */ //循环向前找到一个没有取消的节点 do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); //更新先驱节点 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. */ //// 更新pred结点waitStatus为SIGNAL compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }

parkAndCheckInterrupt()方法,调用LockSupport.park()阻塞指定线程,在AQS中实现

/** * Convenience method to park and then check if interrupted * * @return {@code true} if interrupted */ private final boolean parkAndCheckInterrupt() { //阻塞当前线程,直到其他线程调用LockSupport.unpark(当前线程), //使得调用unpark的线程释放锁后当前线程被唤醒并返回在阻塞期间线程是否被中断 LockSupport.park(this); return Thread.interrupted(); }

cancelAcquire()方法,取消获取锁,在AQS中实现

/** * Cancels an ongoing attempt to acquire. * * @param node the node */ private void cancelAcquire(Node node) { // Ignore if node doesn't exist if (node == null) return; //节点的线程置null node.thread = null; // Skip cancelled predecessors Node pred = node.prev; //如果前驱节点已经取消,那么循环向前找到一个没有取消的节点并设置当前节点的前驱节点 while (pred.waitStatus > 0) node.prev = pred = pred.prev; // predNext is the apparent node to unsplice. CASes below will // fail if not, in which case, we lost race vs another cancel // or signal, so no further action is necessary. Node predNext = pred.next; // Can use unconditional write instead of CAS here. // After this atomic step, other Nodes can skip past us. // Before, we are free of interference from other threads. //设置当前节点状态为已经取消获取锁 node.waitStatus = Node.CANCELLED; // If we are the tail, remove ourselves. //如果节点自身时队尾,则移除节点并使用CAS操作设置队尾 if (node == tail && compareAndSetTail(node, pred)) { compareAndSetNext(pred, predNext, null); } else { // If successor needs signal, try to set pred's next-link // so it will get one. Otherwise wake it up to propagate. int ws; //前驱节点不是头节点,则当前节点就需要阻塞 //前驱节点线程不为null,则保证存在前驱节点 //前驱节点没有被取消,waitStatus可以被设置为SIGNAL保证可以唤醒后继节点 if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; //写一个节点存在并且没有被取消,则CAS的将前驱节点的后继节点设置为当前节点的后继节点 if (next != null && next.waitStatus <= 0) compareAndSetNext(pred, predNext, next); } else { //否则唤醒后继线程 unparkSuccessor(node); } node.next = node; // help GC } }

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

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