AbstractQueuedSynchronizer(AQS)
概括一句话为:可快速实现一个同步容器(比如同步阻塞队列).
ReadWriteLock、ReentrantLock,或者 CountDownLatch 与 Semaphore,甚至是线程池类 ThreadPoolExecutor 都继承了 AQS
他的作用: AQS的实现,依赖她内部的同步队列(FIFO双向链表),当前线程竞争失败,AQS把当前线程+当前线程等待信息构造成node放入同步队列,同事阻塞该线程,当获取锁的线程释放资源后,AQS唤醒一个阻塞节点(线程)。
AQS 内部的数据结构(双向链表)
- 当线程争抢锁失败后会封装成Node加入到AQS队列中去。
AQS 中提供了 state变量做为锁状态,一般来说,0 被视为无锁状态,1 被视为加锁状态,如果是可重入锁,就会大于 1。
因此,AQS 中的加锁解锁实际上就是通过 CAS 改变 state的过程
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
- unsafe是C++实现的用于才做指针的类。 stateoffset是相对偏移地址,expect和update是用来判断是否需要更新值(也就是CAS是否成功), 而需要更新值得地址为stateoffset。
java实现CAS是利用Unsafe类实现,此类用的是C++的方法操作指针,但是会出现ABA的问题,
所以JAVA中提供了AtomicStampedReference/AtomicMarkableReference来处理会发生ABA问题的场景,主要是在对象中额外再增加一个标记来标识对象是否有过变更。
AQS实现独占锁加锁的过程
AQS 的同步过程其实就是同步队列节点中依次获取锁的过程; AQS 一共提供了独占和非独占两种获取资源的方法:
- acquire():以独占模式获取锁;
- release():以独占模式释放锁;
- acquireShared():以共享模式获取锁;
- releaseShared():以共享模式释放锁
独占锁的实现
- 在 synchronize 底层的锁中,独占通过锁对象对象头中的指针来声明 指代独占的线程。
- 在AQS中 则通过父类 AbstractOwnableSynchronizer 提供的 exclusiveOwnerThread 变量来指代当前独占的线程。
AQS通过acquire()
public final void acquire(int arg) {
// 尝试获取锁
if (!tryAcquire(arg) &&
// 添加到等待队列
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 进入等待队列后阻塞
selfInterrupt();
}
在 AQS 中,tryAccquire() 是一个未实现的方法,需要被继承的子类进行自定义方法,比如可重入所ReentrantLook,c=0 代表是第一次锁,c!=0 代表当前线程锁内还要加锁
rotected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果 锁没有被占用
if (c == 0) {
// 如果当前同步队列中没有线程在等待
if (!hasQueuedPredecessors() &&
// 尝试CAS修改state
compareAndSetState(0, acquires)) {
// 将当前锁设为自己独占
setExclusiveOwnerThread(current);
return true;
}
}
// 如果锁已经被自己获取过了,即重入
else if (current == getExclusiveOwnerThread()) {
// state + 1,即多获取一次锁,即可重入锁
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 没有获取锁
return false;
}
好像永远返回true