先做总结:
1、AbstractQueuedSynchronizer是什么?
AbstractQueuedSynchronizer(AQS)这个抽象类,是Java并发包 java.util.concurrent 的基础工具类,是实现 ReentrantLock、CountDownLatch、Semaphore、FutureTask 等类的基础。
AbstractQueuedSynchronizer其实是锁的主体。Lock类会有一个AQS类型的属性,来实现锁。
2、AQS如何实现锁?
(1)Lock类会有一个AQS类型的属性sync,sync是AQS的子类,重写了tryAcquire()方法(获取锁)和tryRelease()方法(释放锁)。
(2)线程T获取到锁标志:AQS.state > 0 && AQS.exclusiveOwnerThread == 当前线程T。tryAcquire()/tryRelease()方法其实就是对AQS.state AQS.exclusiveOwnerThread 的操作。
3、AQS原理:
(1)AQS中维护着一个同步队列。
头结点headNode只能是null或者是已经获取到锁的线程,只有第二个节点能够尝试获取锁。
第二个节点获取到锁之后变为头结点。
(2)acquire()方法会调用tryAcquire()获取锁。tryAcquire()获取到锁,完成。
如果获取锁失败(没有竞争到锁),当前线程T会封装成Node插入同步队列中,并且将当前线程T park()。
(3)release()方法调用tryRelease()方法释放锁,当前线程释放锁之后,会unpark()下一节点(也就是唤醒第二节点,因为持有锁的一定是头节点线程或者不在队列中的线程)
一、CLH同步队列
AQS通过内置的FIFO同步队列来完成资源获取线程的排队工作。
如果当前线程获取同步状态失败(锁)时,AQS则会将当前线程以及等待状态等信息构造成一个节点(Node)并将其加入同步队列,同时会park当前线程;
当同步状态释放时,则会把节点中的线程唤醒,使其再次尝试获取同步状态。
1static final class Node { 2 static final Node SHARED = new Node();// 共享模式 3 static final Node EXCLUSIVE = null;// 独占模式 4 static final int CANCELLED = 1;// 此线程取消了争抢这个锁 5 static final int SIGNAL = -1;// 当前node的后继节点对应的线程需要被唤醒(表示后继节点的状态) 6 static final int CONDITION = -2;// 当前节点线程状态 0-没有获得锁 >0-线程取消了等待 7 volatile int waitStatus; 8 volatile Node prev; 9 volatile Node next; 10 volatile Thread thread;// 每一个节点对应一个线程 11 Node nextWaiter;// 共享模式/独占模式 12}
入列:
1private Node addWaiter(Node mode) { 2 Node node = new Node(Thread.currentThread(), mode); 3 Node pred = tail; 4 if (pred != null) {// 加入队尾 5 node.prev = pred; 6 if (compareAndSetTail(pred, node)) {// 失败:其它线程抢先入列了 7 pred.next = node; 8 return node; 9 } 10 } 11 enq(node); 12 return node; 13 } 14 15 private Node enq(final Node node) { 16 for (;;) {// 循环入列,直到成功 17 Node t = tail; 18 if (t == null) {// 初始化head 19 if (compareAndSetHead(new Node())) 20 tail = head; 21 } else { 22 node.prev = t; 23 if (compareAndSetTail(t, node)) {// 失败:其它线程抢先入列了 24 t.next = node; 25 return t; 26 } 27 } 28 } 29 }
两个方法都是通过一个CAS方法compareAndSetTail(Node expect, Node update)来设置尾节点,该方法可以确保节点是线程安全添加的
二、属性
1private transient volatile Node head; 2 private transient volatile Node tail; 3 private volatile int state;// 0代表没有被占用,大于0代表有线程持有当前锁(锁可以重入,每次重入都+1) 4 private transient Thread exclusiveOwnerThread; // 继承自AbstractOwnableSynchronizer 当前持有锁的线程
三、主要方法
1getState()// 返回同步状态的当前值; 2 setState(int newState)// 设置当前同步状态; 3 compareAndSetState(int expect, int update)// 使用CAS设置当前状态,该方法能够保证状态设置的原子性; 4 5 acquire(int arg)// 独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回,否则,将会进入同步队列等待,该方法将会调用可重写的tryAcquire(int arg)方法; 6 acquireInterruptibly(int arg)// 与acquire(int arg)相同,但是该方法响应中断,当前线程为获取到同步状态而进入到同步队列中,如果当前线程被中断,则该方法会抛出InterruptedException异常并返回; 7 tryAcquireNanos(int arg,long nanos)// 超时获取同步状态,如果当前线程在nanos时间内没有获取到同步状态,那么将会返回false,已经获取则返回true; 8 acquireShared(int arg)// 共享式获取同步状态,如果当前线程未获取到同步状态,将会进入同步队列等待,与独占式的主要区别是在同一时刻可以有多个线程获取到同步状态; 9 acquireSharedInterruptibly(int arg)// 共享式获取同步状态,响应中断; 10 tryAcquireSharedNanos(int arg, long nanosTimeout)// 共享式获取同步状态,增加超时限制; 11 release(int arg)// 独占式释放同步状态,该方法会在释放同步状态之后,将同步队列中第一个节点包含的线程唤醒; 12 releaseShared(int arg)// 共享式释放同步状态; 13 14 tryAcquire(int arg)// 独占式获取同步状态;通过子类重写实现 15 tryRelease(int arg)// 独占式释放同步状态;通过子类重写 16 tryAcquireShared(int arg)// 共享式获取同步状态;子类重写 17 tryReleaseShared(int arg)// 共享式释放同步状态;子类重写
四、以独占锁为例解析AQS(共享锁几乎一样)
AQS的设计模式采用的模板方法模式,子类通过继承的方式,实现它的抽象方法来管理同步状态,对于子类而言它并没有太多的活要做,AQS提供了大量的模板方法来实现同步。
独占式同步状态获取
1public final void acquire(int arg) { 2 if (!tryAcquire(arg) && // 尝试获取锁-由子类重写 3 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 获取锁失败: addWaiter()-当前线程new Node+入列; acquireQueued()-设置前驱节点状态-1,当前线程park() 4 selfInterrupt(); 5 } 6 7 /** 8 * 1.尝试获取锁 获取成功,将当前节点置为头结点 9 * 2.获取锁失败,将前驱节点状态置为-1,当前节点线程park()等待 10 * 3.前驱点释放锁时,会将当前节点unpark(),继续自旋获取锁 11 */ 12 final boolean acquireQueued(final Node node, int arg) { 13 boolean failed = true; 14 try { 15 boolean interrupted = false; // 中断标志 16 17 // 自旋 18 for (;;) { 19 final Node p = node.predecessor(); // 当前线程的前驱节点 20 if (p == head && tryAcquire(arg)) { // 前驱节点是head时,当前节点才能请求锁(请求锁的是第二个节点) 21 setHead(node); // 当前线程获取到锁之后,将当前节点置为头结点 22 p.next = null; 23 failed = false; 24 return interrupted; 25 } 26 27 if (shouldParkAfterFailedAcquire(p, node) && 28 parkAndCheckInterrupt()) 29 interrupted = true; 30 } 31 } finally { 32 if (failed) 33 cancelAcquire(node); 34 } 35 } 36 37 /** 38 * 1.park开头的方法来阻塞当前线程,unpark(Thread thread)方法来唤醒一个被阻塞的线 39 * 2.前驱点 waitStatus==-1,当前节点线程才能park 40 * 3.ws>0表示已经获取过锁,从CLH队列删除,CLH队列存放没有获取到锁被挂起的线程节点 41 * 4.当前线程没有获取到锁,需要设置前驱节点状态为-1,这样当前节点线程才能park() 42 */ 43 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { 44 int ws = pred.waitStatus; // 前驱节点 45 if (ws == Node.SIGNAL) // 当前节点线程可以park 46 return true; 47 if (ws > 0) { // ws>0 已经获取过锁,从CLH队列删除 48 do { 49 node.prev = pred = pred.prev; 50 } while (pred.waitStatus > 0); 51 pred.next = node; 52 } else { // 需要设置前驱节点状态为-1,这样当前节点线程才能park() 53 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); 54 } 55 return false; 56 } 57 58 /** 59 * 当前线程park()等待,直到unpark() 60 */ 61 private final boolean parkAndCheckInterrupt() { 62 LockSupport.park(this); 63 return Thread.interrupted(); 64 } 65
acquire(int arg)方法流程图如下:

独占式同步状态释放
1public final boolean release(int arg) { 2 if (tryRelease(arg)) { // 尝试释放锁-由子类重写 3 Node h = head; // 头结点占有锁(原因:第二节点获取到锁之后被置为头结点) 4 if (h != null && h.waitStatus != 0) 5 unparkSuccessor(h); 6 return true; 7 } 8 return false; 9 } 10 11 private void unparkSuccessor(Node node) { 12 int ws = node.waitStatus; 13 if (ws < 0) 14 compareAndSetWaitStatus(node, ws, 0); 15 Node s = node.next; 16 if (s == null || s.waitStatus > 0) { 17 s = null; 18 // 从队尾往前找,找到waitStatus<=0的所有节点中排在最前面的 19 // 从队尾往前找原因:node.next可能会存在null或者取消了。入列时是先设置node.prev,CAS之后再设置node.next 20 for (Node t = tail; t != null && t != node; t = t.prev) 21 if (t.waitStatus <= 0) 22 s = t; 23 } 24 if (s != null) // 唤醒下一节点 25 LockSupport.unpark(s.thread); 26 }
参考资料 / 相关推荐:
一行一行源码分析清楚AbstractQueuedSynchronizer (超详细,一定能看懂)