AQS是基于CLH队列锁的思想来实现的,其内部不同于CLH单向链表,而是使用的是双向链表。那么对于一个队列来说,其内部一定会通过一个节点来保存线程信息,如:前驱节点、后继节点、当前线程节点、线程状态这些信息。
根据源码可知,AQS内部定义一个Node对象用于存储这些信息。
两种线程等待模式:
-
SHARED:表示线程以共享模式等待锁,如读锁。
-
EXCLUSIVE:表示线程以独占模式等待锁,如写锁。
五种线程状态:
-
初始Node对象时,默认值为0。
-
CANCELLED:表现线程获取锁的请求已经取消,值为1。
-
SINNAL:表现线程已经准备就绪,等待锁空闲给我,值为-1。
-
CONDITION:表示线程等待某一个条件被满足,值为-2。
-
PROPAGETE:当线程处于SHARED模式时,该状态才会生效,用于表示线程可以被共享传播,值为-3。
五个成员变量:
-
waitStatus:表示线程在队列中的状态,值对应上述五种线程状态。
-
prev:表示当前线程的前驱节点。
-
next:表示当前线程的后继节点。
-
thread:表示当前线程。
-
nextWaiter:表示等待condition条件的节点。
同时在AQS中还存在两个成员变量,head和tail,分别代表队首节点和队尾节点
。
节点在同步队列的操作
在多线程并发争抢同步状态(锁)时,按照队列的FIFO原则,AQS会将获取锁失败的线程包装为一个Node放入队列尾部中。
对于加入队列的过程需要保证线程安全,AQS提供了一个基于CAS设置尾节点的方法compareAndSetTail(Node expect,Node update)
。其需要传递当前期望的尾节点和当前节点,当返回true,当前节点才与队列中之前的尾节点建立连接。
此时可以发现头结点一定是获取锁成功的节点,头节点在释放锁时,会唤醒其后继节点。当后继节点获取锁成功后,则头节点的指针会指向该后继节点作为当前队列的头节点,接着将原先的头结点从队列中移除。
对于该流程来说,只有一个线程能够获取到同步状态,因此不需要CAS进行保证。只需要重新移动头部指针并断开原有引用连接即可。