AQS 全称:AbstractQueuedSynchronizer,它是JUC并发工具包中 ReentrantLock 、CountDownLatch、CyclicBarrier等这些类的底层实现。此篇咱们主要经过ReentrantLock的使用来大致了解AQS底层代码设计原理,不对源码详细赘述。只要对总体的设计方向有清晰了解,再去追踪源码便不是什么难事。阅读本篇以前,须要你们对ReentrantLock 的API有必定了解。java
在使用ReentrantLock进行代码块同步,通常都会有以下写法:程序员
ReentrantLock lock = new ReentrantLock(true); Condition condition = lock.newCondition(); try{ lock.lock(); //..... condition.await(); }finally{ lock.unlock(); }
假设 T1 时刻有10个线程调用同一个ReentrantLock实例的lock()方法, 线程1 先获取锁成功,紧接着线程2 调用lock()方法,发现获取锁失败(经过CAS操做对状态位进行标记),则线程2被封装成Node节点放入AQS双向队列中,并调用LockSupport.park()阻塞挂起。一样的线程3,线程4,...线程10 也阻塞挂起加入队列中。至此在t1时刻 AQS的队列如图1-1:并发
T4 时刻线程1执行完毕,调用unlock()方法释放锁并从AQS队列中取出哨兵节点的下一节点,也就是此刻的线程2,并唤醒该线程。线程2再次尝试获取锁,若是锁获取成功将继续执行线程2代码。若是失败会被再次封装成Node节点放入AQS队列中去。看到这里你们可能会有疑问:为何线程1的锁释放完后唤醒的是线程2,而不是线程3 或者线程 4 呢?这里是由于公平锁与非公平锁的缘由,若是采用了公平锁那么将按照先进先出的原则让线程去抢占锁,而非公平锁没有前后顺序的限制。对于ReentrantLock能够经过构造函数的参数进行指定,默认它采用的是非公平锁。函数
咱们紧接着上面的分析,线程T1时刻获取锁成功后,在T2时刻调用了condition.await()方法时会发生如下4件事情:工具
当其余线程调用condition.signal() 或者 condition.sigalAll() 方法时,会将条件队列的一个或者所有Node节点移到AQS队列中。
一个锁对应一个AQS队列、多个condition、多个条件队列,条件队列的个数是跟随Condition走的。咱们经过1-2图来更直观认识 Lock、Condition、AQS队列、条件队列之间的关系。spa
至此ReentrantLock底层使用AQS来实现线程同步的设计已讲解完毕,赶忙撸源码去吧!!!线程
书写技术文章是一个按部就班的过程,因此我不能保证每句话、每行代码都是对的,但至少能保证不复制、不粘贴,每篇文章都是本身对技术的认识、细心斟酌总结出来的。乔布斯说:咱们在这个星球上的时间都很短,不多有机会去作几件真正伟大的事情,同时要作得好,我必需要趁我还年轻的时候完成这些事。 其实我想说的是,我是一枚程序员,我只想在有限的时间内尽量去沉淀我这一辈子中所能沉淀下来的东西。