深入分析同步工具类之AbstractQueuedSynchronizer
概览:
AQS(简称)依赖内部维护的一个FIFO(先进先出)队列,可以很好的实现阻塞、同步;volatile修饰的属性state,哪个线程先改变这个状态值,那么这个线程就获得了优先权,可以做任何事(当然这些事肯定是我们预先写好的需要执行的业务代码咯[坏笑]),而其他线程则会被挂起,直到之前的线程执行完才会轮到下一个。 类库中同步工具类(CountDownLatch[闭锁]、Semaphore[信号量])就是依靠内部维护一个类来更改这个state来实现其自身的特性(说到底还是执行某些线程、阻塞一些线程)
代码分析前:
节点Node几个关键的属性:
1、volatile Thread thread;当前节点代表的线程
2、volatile Node next;当前结点的后继节点
3、volatile Node prev;当前结点的前驱节点
4、volatile int waitStatus;当前结点的等待状态
1 CANCELLED(线程取消)
-1 SIGNAL(表示当前节点的后继节点需要运行)
-2 CONDITION(表示当前节点在等待condition,也就是在condition队列中)
-3 PROPAGATE (表示当前场景下后续的acquireShared能够得以执行)
0(表示当前节点在sync队列中,等待着获取锁)
代码分析:
//获取状态 public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } //该方法由子类去实现(改变state的值) protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); } //为snyc队列添加节点 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; //CAS更新尾节点 if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; } //判断有没有释放状态的线程并且尝试获取状态,或者将线程挂起 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); } }
//线程执行完后释放持有的状态 public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } //子类实现(改变状态的值) protected boolean tryRelease(int arg) { throw new UnsupportedOperationException(); } //将头节点的后继节点的线程恢复,这样后继节点就可以继续执行之前的循环,获取状态。 private void unparkSuccessor(Node node) { /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */ int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) LockSupport.unpark(s.thread); }
参考: