AQS的全称是AbstractQueuedSynchronizer,这是AQS框架的核心抽象类。ReentrantLock有三个内部类:Sync、NonfairSync、FairSync。FairSync代表了公平锁,NonfairSync代表了非公平锁,NonfairSync和FairSync都继承自Sync,Sync继承自AbstractQueuedSynchronizer。
AQS源自Object的wait/notify,所以先要理解Object的wait/notify。
一、synchronized-wait-notify机制
wait():当前线程先要用synchronized获取object或class的监视器锁(后文都简称锁),成功获得锁后,才能调用wait()。wait()此时至少会做三件事:把当前线程放入等待锁的集合、释放锁、当前线程进入阻塞状态。得到通知后,线程被唤醒,wait()继续执行,wait() 再次尝试获得锁,获锁之后才能成功退出wait() 。
notify():当前线程先要获取锁,成功获得锁后,才能执行notify()方法,notify()方法从等待锁的集合中随机选一个线程唤醒。当前线程释放锁后,才能真正执行唤醒。
wait()和notify()都必须用同一把锁。
上面涉及两个集合:等待锁释放的线程集合,等待被唤醒的线程集合。
AQS框架取代了synchronized/wait/notify,把上述两个集合显示化。
二、AQS类结构简介
要理解AQS框架,必然要理解代码逻辑,这里简单讲下类的结构。
AbstractQueuedSynchronizer有四个关键实例变量:
① private volatile int state:代表当前线程进入锁的重入数。
② private transient Thread exclusiveOwnerThread:继承自AbstractOwnableSynchronizer,代表当前获得锁的线程。
③ private transient volatile Node head:同步队列的头节点
④ private transient volatile Node tail:同步队列的尾节点
ConditionObject是AbstractQueuedSynchronizer的内部类,定义了条件队列和等待通知机制。
Node也是AbstractQueuedSynchronizer的内部类,代表了代表同步队列和条件队列的节点。Node有前驱引用和后驱引用,所以同步队列和条件队列都是双向链表。Node有个重要的实例变量waitStatus,和通知机制有关。
三、ReentrantLock加锁主流程
1、公平锁:第一个线程直接获得锁,state从0变为1,不会构建同步队列,exclusiveOwnerThread被赋值为第一个线程的引用,state被赋值为1。第二个线程如果第一次获得锁失败,则会构建同步队列,队列有两个节点,第一个是虚节点代表获得锁的线程,第二个节点封装第二个线程。第二次获得锁失败则会把第一个线程的(前一个队列节点)waitStatus置为-1,代表第一个线程释放锁之后需要通知并唤醒后续节点,然后进入blocking状态。第三个线程如果获得锁失败,则new一个Node节点封装第三个线程加入同步队列,把前一个节点的waitStatus置为-1。
2、非公平锁:先尝试直接获取锁,而不管同步队列。获取失败才会加入同步队列。后续流程同公平锁。
四、ReentrantLock解锁主流程
公平锁和非公平锁的流程是一样的,都是同步队列的头节点出队,并唤醒第二个节点加锁。如果第二个节点被取消,则转为从尾部开始找阻塞的节点。
五、Condition的await()主流程
await()首先判断当前线程是不是已占住锁,占住锁才能继续。然后new一个新的Node实例,封装当前线程,waitStatus的值变为Node.CONDITONAL,该实例加入条件队列尾部。然后释放锁,再调用LockSupport.park()进入阻塞状态。
在当前线程释放锁之前,当前线程必然是同步队列的第一个节点。释放锁之后,当前线程就从同步队列出队了。
被唤醒后,再次尝试获取锁。获取锁之后退出await()方法。
六、Condition的 signal()主流程
signal()首先判断当前线程是不是已占住锁,占住锁才能继续。然后把条件队列首节点的waitStatus置为0,然后该首节点从条件队列出队。出队后的节点不会被垃圾回收,而是加入同步队列尾部,把同步队列的倒数第二个节点的waitStatus置为-1,再用LockSupport.unpark()唤醒出队节点代表的线程。
上述分析忽略了响应中断的流程。欢迎讨论、质疑、指正。