一文带你学会AQS和并发工具类的关系 (3)

tryAcquire(arg) 该方法是protected的由子类去具体实现的

图片

图片

  我们需要看的是NonfairSync中实现的tryAcquire方法,里面又调用了nonfairTryAcquire方法,再进去看看

static final class NonfairSync extends Sync { protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }

nonfairTryAcquire(int acquires) 方法实现

final boolean nonfairTryAcquire(int acquires) { // 获取当前线程 final Thread current = Thread.currentThread(); // 获取当前state的值 int c = getState(); if (c == 0) { // 看看设置值是否能成功 if (compareAndSetState(0, acquires)) { // 则将当前线程设置为独占线程 setExclusiveOwnerThread(current); return true; } } // 返回由setExclusiveOwnerThread设置的最后一个线程;如果从不设置,则返回null  else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); // 设置state的值 setState(nextc); return true; } return false; }

acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 方法实现,先看里addWaiter(Node.EXCLUSIVE)方法注意:Node.EXCLUSIVE 此时是空值,所以mode 就是空的,所以此时创建的Node节点中的nextWaiter是空值。

static final class Node { Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } } private Node addWaiter(Node mode) { // 创建一个新的节点 Node node = new Node(Thread.currentThread(), mode); // 将当前CLH队列的尾部节点赋予给 pred Node pred = tail; if (pred != null) { // 如果尾节点不为空 node.prev = pred; // 将当前node节点的前驱节点指向CLH队列的尾部节点 if (compareAndSetTail(pred, node)) { // CAS设置值 pred.next = node; // CLH队列的尾部节点的后继节点指向新的node节点 return node; } } enq(node); return node; }

如果CLH队列的尾部节点为空值的话,执行enq(node)方法

// 通过CAS方式设置队列的头节点 private final boolean compareAndSetHead(Node update) { return unsafe.compareAndSwapObject(this, headOffset, null, update); } // 通过CAS方式设置队列的尾部节点 private final boolean compareAndSetTail(Node expect, Node update) { return unsafe.compareAndSwapObject(this, tailOffset, expect, update); } // 节点入队操作 private Node enq(final Node node) { for (;;) { Node t = tail; // 如果尾部节点为空值 if (t == null) { // 则进行初始化一个节点,将其设置为头节点 if (compareAndSetHead(new Node())) tail = head; //头尾指向同一个空节点 } else { // 如果尾部节点不为空,那么将传进来的入队节点的前驱节点指向当前队列的尾部节点 node.prev = t; // 将当前传进来的入队节点设置为当前队列的尾部节点 if (compareAndSetTail(t, node)) { // 将当前队列的尾部节点的后继节点指向传进来的新节点 t.next = node; return t; } } } }

查看 acquireQueued 方法实现

// 获取当前节点的前驱节点 final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } // 检查并更新无法获取的节点的状态。 如果线程应阻塞,则返回true private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //SIGNAL这个状态就有点意思了,它不是表征当前节点的状态,而是当前节点的下一个节点 //的状态。当一个节点的waitStatus被置为SIGNAL,就说明它的下一个节点(即它的后继 // 节点)已经被挂起了(或者马上就要被挂起了),因此在当前节点释放了锁或者放弃获取 // 锁时,如果它的waitStatus属性为SIGNAL,它还要完成一个额外的操作——唤醒它的后继节点。 int ws = pred.waitStatus; if (ws == Node.SIGNAL) return true; if (ws > 0) { // 当前节点的 ws > 0, 则为 Node.CANCELLED 说明前驱节点 // 已经取消了等待锁(由于超时或者中断等原因) // 既然前驱节点不等了, 那就继续往前找, 直到找到一个还在等待锁的节点 // 然后我们跨过这些不等待锁的节点, 直接排在等待锁的节点的后面 do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { // 前驱节点的状态既不是SIGNAL,也不是CANCELLED // 用CAS设置前驱节点的ws为 Node.SIGNAL,给自己定一个闹钟 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } // 停止的便捷方法,然后检查是否中断 private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); } // 取消正在进行的获取尝试 private void cancelAcquire(Node node) { if (node == null) return; node.thread = null; Node pred = node.prev; while (pred.waitStatus > 0) node.prev = pred = pred.prev; Node predNext = pred.next; node.waitStatus = Node.CANCELLED; if (node == tail && compareAndSetTail(node, pred)) { compareAndSetNext(pred, predNext, null); } else { int ws; if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; if (next != null && next.waitStatus <= 0) compareAndSetNext(pred, predNext, next); } else { unparkSuccessor(node); } node.next = node; } } // 能执行到该方法, 说明addWaiter 方法已经成功将包装了当前Thread的节点添加到了等待队列的队尾 // 该方法中将再次尝试去获取锁 // 在再次尝试获取锁失败后, 判断是否需要把当前线程挂起 final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { // 获取当前节点的前驱节点 final Node p = node.predecessor(); // 在前驱节点就是head节点的时候,继续尝试获取锁 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } // 将当前线程挂起,使CPU不再调度它 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }

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

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