学习完子路老师的课之后自己分析的,可能存在问题。。。
AQS源码阅读
ReentrantLock里有内部得Sync类继承AQS,再有FairSync和NonFairSync继承Sync实现公平和非公平锁,下面阅读公平锁
FairSync有个lock方法
final void lock() {
acquire(1);//这个1是AQS得status是1
}
然后咱们进入acquire方法,该方法再AQS里实现
public final void acquire(int arg) {
if (!tryAcquire(arg) //尝试获取锁
&& acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //加入等待队列
selfInterrupt(); //可中断锁
}
可以看到这里是两个判断,咱们先看看 tryAcquire得实现,注意if里对该方法得返回值取反了,实现再FairSync里
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();//得到当前线程
int c = getState(); //得到当前得status
if (c == 0) { //如果当前status为0,则代表没人拿着锁,然后就CAS占有锁
if (!hasQueuedPredecessors() //等会儿进去看看
&& compareAndSetState(0, acquires)) //这个就好说了CAS
{
setExclusiveOwnerThread(current); //设定为持有锁为当前线程,占有锁成功
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
我们刚才看到了hasQueuePredecessors(),在这里卡住,现在进入这个方法
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
这里可以看到Node这个类,然后再看Node类,只看几个重点得
static final class Node {
volatile int waitStatus; //代表当前线程得等待状态
volatile Node prev; //前一个节点
volatile Node next; //后一个节点
volatile Thread thread; //节点当前代表得线程
}
这个Node就是队列得节点类,实现是个deque,默认没有被初始化,
private transient volatile Node head;
private transient volatile Node tail;
借用子路老师名言,一切看源码,源码证明一切。
继续回到hasQueuePredecessors()
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
//这里在第一个节点过来得时候,head和tail都为空,此时 h == t,而return得是h!=t,
//结果就为false,返回,然后往回看
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
//第一个等待线程不为空且当前线程是第一个等待线程才让它去获取
}
回到了tryAcquire方法
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();//得到当前线程
int c = getState(); //得到当前得status
if (c == 0) { //如果当前status为0,则代表没人拿着锁,然后就CAS占有锁
if (!hasQueuedPredecessors()
//好了现在可以确定这里是false,取反就是true,然后执行下面得判断CAS
&& compareAndSetState(0, acquires)) //这个就好说了CAS
{
setExclusiveOwnerThread(current); //设定为持有锁为当前线程,占有锁成功
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
现在假设CAS成功,那么该方法返回为true,代表该线程占有锁成功,然后回到acquire方法
public final void acquire(int arg) {
if (!tryAcquire(arg)
//刚才说到tryAcquire返回为true,但是if取!了,所以不判断后面直接完事,方法返回
&& acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
返回回到Lock方法
final void lock() {
acquire(1);
}
acquire执行完毕,lock返回,源码处继续向下执行。。。
终于最简单得情况分析完毕,下面回到刚才得分支出,也就是tryAcquire里c不为0,也就是锁被占用着得时候
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();//得到当前线程
int c = getState(); //得到当前得status,刚才是0,现在是1
if (c == 0) {
if (!hasQueuedPredecessors()
&& compareAndSetState(0, acquires))
{
setExclusiveOwnerThread(current);
return true;
}
}
//C为1进入这里,current是否为当前锁的持有者??
//如果是,就进入,先假设是,也就是锁重入的过程分析
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
//nextc的意思是锁的引用计数+1,之后unlock要用到
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);//设定为新的引用技术
return true;
//返回true,这里不再赘述返回过程,和刚才返回true一样,代码继续向下进行
}
return false;
}
这就是ReentrantLock锁重入的过程分析,好咱们继续回到上一个分支,如果当前线程不是持有锁的线程
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();//得到当前线程
int c = getState(); //得到当前得status,刚才是0,现在是1
if (c == 0) {
if (!hasQueuedPredecessors()
&& compareAndSetState(0, acquires))
{
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//没进if 也没进 else,直接返回false
return false;
}
返回false后,我们返回到acquire方法
public final void acquire(int arg) {
if (!tryAcquire(arg) //tryAcquire为false,取反为true,然后进入下一个的判断
&&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
先看addWaiter(Node.EXCLUSIVE)方法
private Node addWaiter(Node mode) {
//mode刚才传入的是Node.EXCLUSIVE此时为NULL,在Node节点的定义里可以看到
Node node = new Node(Thread.currentThread(), mode);
//创建一个该线程的节点
// Try the fast path of enq; backup to full enq on failure
Node pred = tail; //这个节点的前驱是原来的尾节点
if (pred != null) { //前驱不为空,则代表队列里有元素
node.prev = pred; //连接该节点的prev到前驱节点
if (compareAndSetTail(pred, node)) {//CAS方式设置新的tail
pred.next = node;
return node; //返回当前尾节点
}
}
enq(node);
return node;
}
现在是队列不为空的情况,下面是pred如果为null,那么将会进入enq方法,看看enq方法干了什么
private Node enq(final Node node) {
for (;;) {//死循环
Node t = tail; //当前的尾节点
if (t == null) { // 如果为null,则代表需要初始化操作
if (compareAndSetHead(new Node())) //cas创建头节点
tail = head;//尾节点指向头节点
} else {
//如果已经被初始化了,就是第一次循环过后尾节点就不空了,第二次循环就会进入到这里
node.prev = t;
if (compareAndSetTail(t, node)) {
//CAS把当前节点设定为尾节点,队列有个为null的空节点
//代表着正拿着锁的那个线程,不理解之后再说
t.next = node;
return t;
}
}
}
}
总之,addWaiter是返回当前新的尾节点,那我们在回到acquire
public final void acquire(int arg) {
if (!tryAcquire(arg) //tryAcquire为false,取反为true,然后进入下一个的判断
&&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
下面分析acquireQueued方法,把当前新的节点(里面是当前线程)作为参数传入
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true; //我也不懂,之后再说
try {
boolean interrupted = false; //这个之后锁中断回用到,现在用不到
for (;;) {
//死循环
final Node p = node.predecessor();
//找到当前节点的前驱节点,这里需要注意一下当前处于等待状态的第一个线程
//第一个线程如果在等待,后面直接去睡觉不久行了,第一个都没拿到轮不到后面的
//看下面这个if,p==head,现在假设当前线程就是第一个等待的线程,
//刚才分析过 tryAcquire 的作用是通过CAS的方式竞争锁,这里的意思就是自旋一次,
//假设这次自旋是拿到锁了,就进入了这个if
if (p == head && tryAcquire(arg)) {
setHead(node); //设定新头
p.next = null; // 取消引用,让GC回收
failed = false; //
return interrupted;//返回interrupted我也不知道为啥,再看看
}
//这次自旋没拿到锁,走到这里,这里需要预备park的前备知识
//看看这个方法名 应该停下在获取失败后,也就很好理解了,先进去看一下
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
进去shouldParkAfterFailedAcquire(p, node)瞅瞅
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//看看前一个节点的status,现在是第一个线程,pred也就是head节点
//刚才头节点在初始化的时候,waitStatus没有赋值,默认为0
if (ws == Node.SIGNAL) // Node.SIGNAL == -1
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//为0进入到这里,把前一个节点的waitStatus设为-1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
//返回false
return false;
}
waitStatus代表的是前一个thread的状态,每次新的线程过来的时候都会检查一下前一个的状态,如果前一个在睡,那它直接睡觉就行,这个方法返回了,那我们回到上一个方法。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true; //我也不懂,之后再说
try {
boolean interrupted = false; //这个之后锁中断回用到,现在用不到
for (;;) {
//死循环
final Node p = node.predecessor();
//找到当前节点的前驱节点,这里需要注意一下当前处于等待状态的第一个线程
//第一个线程如果在等待,后面直接去睡觉不久行了,第一个都没拿到轮不到后面的
//看下面这个if,p==head,现在假设当前线程就是第一个等待的线程,
//刚才分析过 tryAcquire 的作用是通过CAS的方式竞争锁,这里的意思就是自旋一次,
//假设这次自旋是拿到锁了,就进入了这个if
if (p == head && tryAcquire(arg)) {
setHead(node); //设定新头
p.next = null; // 取消引用,让GC回收
failed = false; //
return interrupted;//返回interrupted我也不知道为啥,再看看
}
//这次自旋没拿到锁,走到这里,这里需要预备park的前备知识
//看看这个方法名 应该停下在获取失败后,也就很好理解了,先进去看一下
//现在shouldParkAfterFailedAcquire(p,node)返回的是false,此次循环结束
//开启下一次循环
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
//下一次循环的情况
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true; //我也不懂,之后再说
try {
boolean interrupted = false; //这个之后锁中断回用到,现在用不到
for (;;) {
//死循环
final Node p = node.predecessor();
//这里如果是第一个等待节点,那它就再自旋1次,总计自旋两次
if (p == head && tryAcquire(arg)) {
setHead(node); //设定新头
p.next = null; // 取消引用,让GC回收
failed = false; //
return interrupted;//返回interrupted我也不知道为啥,再看看
}
//然后就又进入到了 shouldParkAfterFailedAcquire 方法
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
进入shouldParkAfterFailedAcquire
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//看看前一个节点的status,现在是第一个线程,pred也就是head节点
//刚才的时候把status设定为了-1,-1就是等待的状态
if (ws == Node.SIGNAL) // Node.SIGNAL == -1
//进入这里 返回true
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//为0进入到这里,把前一个节点的waitStatus设为-1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
//返回false
return false;
}
返回到acquireQueued
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true; //我也不懂,之后再说
try {
boolean interrupted = false; //这个之后锁中断回用到,现在用不到
for (;;) {
//死循环
final Node p = node.predecessor();
//这里如果是第一个等待节点,那它就再自旋1次,第一个总计自旋3次
if (p == head && tryAcquire(arg)) {
setHead(node); //设定新头
p.next = null; // 取消引用,让GC回收
failed = false; //
return interrupted;//返回interrupted我也不知道为啥,再看看
}
//shouldParkAfterFailedAcquire返回了true,进行第二个的判断
//parkAndCheckInterrupt
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
进入parkAndCheckInterrupt
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); //把当前线程park,此时应该再这里被阻塞掉了,该方法不返回
return Thread.interrupted(); //返回当前线程是否处于interrupted状态
}
以上就是第一个等待节点的全过程,哈哈哈哈,终于分析到这儿了,这个线程现在被阻塞着在等待队列里头呆着
第二个节点的分析就会简单很多,自行分析
下面是解锁的过程
public void unlock() {
sync.release(1);
}
进入release,在AQS里
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
先tryRelease了,进入tryRelease,这个在Sync类哈,注意Sync类里!!!
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
//当前status与应当的1作比较,如果>0则代表有重入,不可以释放掉
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
//是0,可以释放
free = true;
setExclusiveOwnerThread(null);//释放锁
}
setState(c);
return free;
}
这样就可以理解了tryRelease如果发生了锁重入,则不会完全释放,如果没有锁重入,就会返回true
就可以进行锁的释放
if (tryRelease(arg)) {
//tryRelease为true的情况
Node h = head;
if (h != null && h.waitStatus != 0)
//如果当前不为空,并且waitStatus不为0,为0的话就代表了后面没有新进来等待的线程
unparkSuccessor(h);//进入unparkSuccessor
return true;
}
return false;
进入unparkSuccessor
private void unparkSuccessor(Node node) {
int ws = node.waitStatus; //当前节点的等待状态
if (ws < 0) //如果当前节点的状态<0,那么久进行CAS把当前节点的waitStatus置为0
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next; //获取第一个等待线程,如果有等就是第二个,不是第二个的可能性不大
if (s == null || s.waitStatus > 0) { //若为空,或者等待状态 > 0
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
//从后往前找找到第一个小于0的
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
//解锁这个线程
LockSupport.unpark(s.thread);
}