(1)AQS的定位?java
(2)AQS的重要组成部分?git
(3)AQS运用的设计模式?面试
(4)AQS的整体流程?设计模式
AQS的全称是AbstractQueuedSynchronizer,它的定位是为Java中几乎全部的锁和同步器提供一个基础框架。框架
在以前的章节中,咱们一块儿学习了ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch的源码,今天咱们一块儿来对AQS作个总结。学习
AQS中定义了一个状态变量state,它有如下两种使用方法:ui
(1)互斥锁spa
当AQS只实现为互斥锁的时候,每次只要原子更新state的值从0变为1成功了就获取了锁,可重入是经过不断把state原子更新加1实现的。线程
(2)互斥锁 + 共享锁设计
当AQS须要同时实现为互斥锁+共享锁的时候,低16位存储互斥锁的状态,高16位存储共享锁的状态,主要用于实现读写锁。
互斥锁是一种独占锁,每次只容许一个线程独占,且当一个线程独占时,其它线程将没法再获取互斥锁及共享锁,可是它本身能够获取共享锁。
共享锁同时容许多个线程占有,只要有一个线程占有了共享锁,全部线程(包括本身)都将没法再获取互斥锁,可是能够获取共享锁。
AQS中维护了一个队列,获取锁失败(非tryLock())的线程都将进入这个队列中排队,等待锁释放后唤醒下一个排队的线程(互斥锁模式下)。
AQS中还有另外一个很是重要的内部类ConditionObject,它实现了Condition接口,主要用于实现条件锁。
ConditionObject中也维护了一个队列,这个队列主要用于等待条件的成立,当条件成立时,其它线程将signal这个队列中的元素,将其移动到AQS的队列中,等待占有锁的线程释放锁后被唤醒。
Condition典型的运用场景是在BlockingQueue中的实现,当队列为空时,获取元素的线程阻塞在notEmpty条件上,一旦队列中添加了一个元素,将通知notEmpty条件,将其队列中的元素移动到AQS队列中等待被唤醒。
AQS这个抽象类把模板方法设计模式运用地炉火纯青,它里面定义了一系列的模板方法,好比下面这些:
// 获取互斥锁
public final void acquire(int arg) {
// tryAcquire(arg)须要子类实现
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 获取互斥锁可中断
public final void acquireInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// tryAcquire(arg)须要子类实现
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
// 获取共享锁
public final void acquireShared(int arg) {
// tryAcquireShared(arg)须要子类实现
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// 获取共享锁可中断
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// tryAcquireShared(arg)须要子类实现
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
// 释放互斥锁
public final boolean release(int arg) {
// tryRelease(arg)须要子类实现
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// 释放共享锁
public final boolean releaseShared(int arg) {
// tryReleaseShared(arg)须要子类实现
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
复制代码
获取锁、释放锁的这些方法基本上都穿插在ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch的源码解析中了,如今看他们是否是舒服多了,若是一开始就看这些源码,不免会很晕。
上面一块儿学习了AQS中几个重要的模板方法,下面咱们再一块儿学习下几个须要子类实现的方法:
// 互斥模式下使用:尝试获取锁
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
// 互斥模式下使用:尝试释放锁
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
// 共享模式下使用:尝试获取锁
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
// 共享模式下使用:尝试释放锁
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
// 若是当前线程独占着锁,返回true
protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
}
复制代码
这几个方法为何不直接定义成抽象方法呢?
由于子类只要实现这几个方法中的一部分就能够实现一个同步器了,因此不须要定义成抽象方法。
今天咱们大概讲了下AQS中几个重要的组成部分,搞明白了这几个结构,AQS对你将没有任何秘密可言,固然面试的时候能把这几个点答清楚,面试官也会眼前一亮的。
(1)状态变量state;
(2)AQS队列;
(3)Condition队列;
(4)模板方法;
(5)须要子类实现的方法;
通过前面的学习,您能简要描述一下AQS获取互斥锁的大致流程吗?
这里彤哥就不做答了,相信学习完前面的内容,答这道题不是个问题了,答不上来的还须要把下面的推荐阅读好好多看几遍^^
三、 死磕 java同步系列之JMM(Java Memory Model)
八、 死磕 java同步系列之ReentrantLock源码解析(一)——公平锁、非公平锁
九、 死磕 java同步系列之ReentrantLock源码解析(二)——条件锁
十、 死磕 java同步系列之ReentrantLock VS synchronized
十一、 死磕 java同步系列之ReentrantReadWriteLock源码解析
1三、 死磕 java同步系列之CountDownLatch源码解析
欢迎关注个人公众号“彤哥读源码”,查看更多源码系列文章, 与彤哥一块儿畅游源码的海洋。