addWaiter方法,首先会初始化一个node节点,将当前线程设置到node节点中。然后判断head和tail节点是否为空,head和tail节点是懒加载的,当AQS初始化时为null,则第一次进来时if (pred != null) 条件不成立,执行enq方法。
例子说明
假如t1和t2线程同时执行到该方法,head节点未初始化则执行enq。
private Node addWaiter(Node mode) { 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; } enq此时可能多个线程会同时调用enq方法,所以该方法中也使用CAS操作。for (;;)是个死循环,首先会CAS操作初始化head节点,且head节点是个空节点,没有设置线程。然后第二次循环时通过CAS操作将该节点设置我尾部节点,并将前置节点指向head,之后会跳出循环,返回生成的Node节点到addWaiter,从源码可以看到addWaiter方法后面没有逻辑,之后会调用acquireQueued。
例子说明
t1和t2线程同时执行,t1线程上天眷顾CAS成功,则流程为
初始化head
t1线程的node节点加入等待队列
t2线程执行,node节点加入等待队列
private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { // Must initialize if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } } addWaiter分支2现在在来说t3和t4,t3和t4线程这时终于获取到了cpu的执行权,此时head节点已经初始化,则进入条件中的代码,其实也是通过CAS操作将节点加入到等待队列尾部,之后会调用acquireQueued。
例子说明
假如t3线程先CAS成功,之后t4成功,此时的数据结构为
private Node addWaiter(Node mode) { 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; } acquireQueued这个方法有两个逻辑,首先如果该节点的前置节点是head会走第一个if,再次去尝试获取锁???
获取锁成功,则将头节点设置为自己,并返回到acquire方法,此时acquire方法执行完,代表获取锁成功,线程可以执行自己的逻辑了。这里有下面几个注意点
p.next = null; // help GC 设置旧的head节点的后置节点为null
setHead方法 将t1节点设置为头节点,因为头节点是个空节点,所以设置t1线程节点线程为null,设置t1前置节点为null,此时旧的head节点已经没有任何指向和关联,可以被gc回收,所以上面那一步会写个help GC 的注释。
例子说明
现在t1线程的前置节点为头结点,如果t1执行tryAcquire成功则结果为
当获取锁失败或者前置节点不是头节点都会走第二个if逻辑,首先会判断当前线程是否需要挂起,如果需要则执行线程挂起。
final boolean acquireQueued(final Node node, int arg) { 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; } } finally { if (failed) cancelAcquire(node); } } private void setHead(Node node) { head = node; node.thread = null; node.prev = null; } shouldParkAfterFailedAcquire