AQS概述
AQS 是JUC并发包中ReentrantLock,ReentrantReadWriteLock,CountDownLatch,CyclicBarrier等类的底层实现。
AQS主要是维护了一个CLH阻塞队列(双向队列)
这个队列中保存的是没有获得锁的线程。
获取锁资源
线程A
ReentrantLock lock = new ReentrantLock(true);
Condition condition = lock.newCondition();
try{
lock.lock();
//.....
condition.await();
}finally{
lock.unlock();
}
具体过程如下:
- 1.有A,B两个线程去获取资源,A线程通过Lock方法获取锁资源。
- 2.B线程接着去调用Lock方法想获取已经属于A的锁资源。
- 3.B线程会因为获取锁失败(通过CAS操作对状态位进行标记来获取锁),被封装成Node节点放入到AQS CLH双向队列中
- 4.调用LockSupport.park()将线程阻塞挂起。
- 5.同理其他再想争用线程A锁的线程也会被加入CLH队列中,并被挂起。
condition
我们知道Lock和synchronized的一大区别就是Lock支持条件的,Lock和condition的关系是1:n的,即一个lock可以有多个条件
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
条件阻塞
Lock和使用condition步骤如下:
- 1.线程A需要通过lock方法先获得lock锁。
- 2.当线程A调用到condition.await()释放锁(通过CAS操作对状态进行标记)
- 3.阻塞线程A
- 4.线程A封装成node节点放入条件队列中(每个条件有自己的条件队列,和AQS的CLH队列无关)
- 5.由于资源被释放,因此去AQS中唤起一个之前被阻塞挂起的线程。
条件释放
try
{
lock.lock();
...
condition.signal();
}
finally
{
lock.unlock();
}
- 1.已经有线程A因为lock的条件被加入到条件队列,并且被挂起(但是不会将线程加入AQS阻塞队列,因为条件尚未满足)。同时线程A释放lock锁
- 2.线程B获取lock锁
- 3.线程B之行condition.signal将原本已经因为条件不满足加入到条件队列的线程B取出。并将线程B加入到AQS阻塞队列中
- 4.当线程B之行unlock操作,阻塞队列中的线程A可以进行lock的资源争抢。
AQS概述已经讲完了,下篇介绍下AQS源码层面相关。