首先看下AQS以及CLH
在AQS(AbstractQueuedSynchronizer)中,没有获取到锁的线程我们使用CLH队列进行储存
CLH是一个双向链表,链表的元素是Node节点,Node类是AQS的内部类,Node主要存储以下信息:
共享模式的标记
static final Node SHARED = new Node();
独占模式的标记
static final Node EXCLUSIVE = null;
waitStatus变量的值,标志着线程被取消
static final int CANCELLED = 1;
waitStatus变量的值,标志着后继线程(即队列中此节点之后的节点)需要被阻塞.(用于独占锁)
static final int SIGNAL = -1;
waitStatus变量的值,标志着线程在Condition条件上等待阻塞.(用于Condition的await等待)
static final int CONDITION = -2;
waitStatus变量的值,标志着下一个acquireShared方法线程应该被允许。(用于共享锁)
static final int PROPAGATE = -3;
标记着当前节点的状态,默认状态是0, 小于0的状态都是有特殊作用,大于0的状态表示已取消
volatile int waitStatus;
prev和next实现一个双向链表
volatile Node prev;
volatile Node next;
该节点拥有的线程
volatile Thread thread;
两种作用:
1. 表示下一个在Condition条件上等待的节点
2. 表示是共享模式或者独占模式,注意第一种情况节点一定是共享模式
Node nextWaiter;
通过ReentrantLock加锁时会调用内部类Sync的lock方法,Sync有两个子类NonfairSync(非公平锁)和FairSync(公平锁),
子类重写了父类的lock方法,在lock方法中会调用Sync的父类AbstractQueuedSynchronizer的acquire方法,这个方法完成两步操作:
第一步
该方法首先会回调孙子类NonfairSync或FairSync的tryAcquire方法,然后调用Sync的nonfairTryAcquire方法,
这个方法会获取当前的state值(该值理解成信号量,标记了当前有多少个信号量可以使用),
如果发现state值为0,那么就通过CAS操作更新state的值,默认更新为1;
如果不为零,那么判断当前线程是不是独占锁拥有的的线程,如果是,就在原来的state值上默认加1
第二步
调用acquireQueued方法
先调用addWaiter方法为当前线程创建一个节点node,并插入队列中,
然后调用acquireQueued方法去获取锁,该方法会取当前节点和当前节点的上一个节点,
如果没有上一节点,尝试获取锁,获取成功了就返回
如果不成功或者有上一节点,就会执行shouldParkAfterFailedAcquire方法,
该方法会判断当前节点的上一节点的waitStatus是否为-1(阻塞),如果为-1,则调用LockSupport.pack(this)来阻塞该节点线程;
如果当前节点的上一节点的waitStatus大于0(即取消),那么就移除该节点;
如果当前节点的上一节点的waitStatus等于0,那么就把他的waitStatus设置为-1(阻塞)
所以这里的阻塞就是通过LockSupport来实现的,唤醒也同样是通过LockSupport来实现的