什么是AQS
Abstract Queued Synchronize(抽象队列同步器)
总起来说就是一些API组件,为了完成像sychnorized的互斥锁,线程阻塞等的一些功能。
synchronized
我们知道synchronized实现互斥锁是通过锁住一个对象完成互斥锁,也就是在对象头中保存线程的地址,如果有其他想锁住对象的时候,会进行等待(通过切换到内核态改变状态)。而重入则是每一次相同的线程进入都把计数器加1,退出减1。
模拟synchronized
- 我们通过java代码是无法直接让线程进入阻塞状态,但是JDK中内置了LockSupport进行管理线程的挂起和执行。
- 实现互斥锁,通过一个变量指向进入的线程,如果第二次进入的线程不是以保存的线程,则通过LockSupport挂起。
- 实现重入,指定一个变量开始的时候为0,当第一个线程进入的时候,变量设置为1,同时把第二部中的变量设置为进入的线程,再一次有线程进入,如果是保存的线程变量加1,否则挂机,并用定义的链表或者集合保存挂起的线程。
- 实现线程的阻塞(wait()),使用LockSupport挂起线程,并用另一个队列保存。
综上所述模拟synchronized的几步,就是JDK中AQS的实现方法,我们所说的AQS也就是这两个队列。
实例
如上面两图所示,state表示进入的线程数,而exclusiveOwerThread存储的则是可以进入的线程。
源码
上面知识大致介绍了一下AQS是什么,领略一下JDK的实现吧,代码只涉及主要代码,详细看的朋友自行查看。
lock()
final void lock() {
// 通过CAS操作给state赋值1,如果成功说明之前没有线程锁定
if (compareAndSetState(0, 1))
// exclusiveOwnerThread设置为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 如果设置失败,已经有线程占用
acquire(1);
}
acquire方法:
public final void acquire(int arg) {
// tryAcquire查看是否是当前线程,如果是state加1,不是则返回false
if (!tryAcquire(arg) &&
// acquireQueued设置挂起线程,详情见下面
// addWaiter添加一个节点,放入队列中
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire()中间有一个方法略过:
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取state的值
int c = getState();
// 再一次判断,可能在这个调用过程中,之前的线程已经退出
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
// 如果是当前线程,则state加1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
acquireQueued():
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;
}
// 将状态值设置为-1,成功则返回true
if (shouldParkAfterFailedAcquire(p, node) &&
// 通过Thread.park中断当前线程,循环结束。
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
实现lock的代码如上,unlock,Condation.await和signal,signalAll等代码就不往上写了,都不是很难,大家自己看即可。
总结
简单介绍了AQS的大致实现过程,如果向深入了解互斥锁的实现,点击这里
加油!