// 加锁方法,传参是1
public final void acquire(int arg){//1. 首先尝试获取锁,如果获取成功,则设置state+1,exclusiveOwnerThread=currentThread(留给子类实现)
if (!tryAcquire(arg)&&//2. 如果没有获取成功,把线程组装成Node节点,追加到同步队列末尾
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)){//3. 加入同步队列后,将自己挂起
selfInterrupt();}}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
再看一下addWaiter方法源码,作用就是把线程组装成Node节点,追加到同步队列末尾。
// 追加到同步队列末尾,传参是共享模式or排他模式
private Node addWaiter(Node mode){//1. 组装成Node节点
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred !=null){
node.prev= pred;//2. 在多线程竞争不激烈的情况下,通过CAS方法追加到同步队列末尾
if (compareAndSetTail(pred, node)){
pred.next= node;
return node;}}//3. 在多线程竞争激烈的情况下,使用死循环保证追加到同步队列末尾
enq(node);
return node;}// 创建Node节点,传参是线程,共享模式or排他模式
Node(Thread thread, Node mode){
this.thread= thread;
this.nextWaiter= mode;}// 通过死循环的方式,追加到同步队列末尾
private Node enq(final Node node){
for (;;){
Node t = tail;
if (t ==null){
if (compareAndSetHead(new Node()))
tail = head;} else {
node.prev= t;
if (compareAndSetTail(t, node)){
t.next= node;
return t;}}}}
// 加入同步队列后,找到能将自己唤醒的节点
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node){int ws = pred.waitStatus;//1. 如果前驱节点的状态已经是SIGNAL状态(释放锁后,需要唤醒后继节点),就无需操作了
if (ws == Node.SIGNAL)
return true;//2. 如果前驱节点的状态是已取消,就继续向前遍历
if (ws >0){
do {
node.prev= pred = pred.prev;} while (pred.waitStatus>0);
pred.next= node;} else {//3. 找到了不是取消状态的节点,把该节点状态设置成SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}
return false;}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
从代码中可以很清楚的看到,目的就是为了找到不是取消状态的节点,并把该节点的状态设置成SIGNAL。
状态是SIGNAL的节点,释放锁后,需要唤醒其后继节点。
简单理解就是:小弟初来乍到,特意来知会老大一声,有好事,多通知小弟。
再看一下释放锁的逻辑。
4.2 释放锁
释放锁的流程如下:
释放锁的代码逻辑比较简单:
// 释放锁
public final boolean release(int arg){//1. 先尝试释放锁,如果时候成功,则设置state-1,exclusiveOwnerThread=null(由子类实现)
if (tryRelease(arg)){
Node h = head;//2. 如果同步队列中还有其他节点,就唤醒下一个节点
if (h !=null&& h.waitStatus!=0)//3. 唤醒其后继节点
unparkSuccessor(h);
return true;}
return false;}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
再看一下唤醒后继节点的方法
// 唤醒后继节点
private void unparkSuccessor(Node node){int ws = node.waitStatus;//1. 如果头节点不是取消状态,就重置成初始状态
if (ws <0)
compareAndSetWaitStatus(node, ws,0);
Node s = node.next;//2. 如果后继节点是null或者是取消状态
if (s ==null|| s.waitStatus>0){
s =null;//3. 从队尾开始遍历,找到一个有效状态的节点
for (Node t = tail; t !=null&& t != node; t = t.prev)
if (t.waitStatus<=0)
s = t;}//3. 唤醒这个有效节点
if (s !=null)
LockSupport.unpark(s.thread);}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
4.3 await等待
await等待的流程:
持有锁的线程可以调用await方法,作用是:释放锁,并追加到条件队列末尾。
// 等待方法
public final void await() throws InterruptedException {// 如果线程已中断,则中断
if (Thread.interrupted())
throw new InterruptedException();//1. 追加到条件队列末尾
Node node = addConditionWaiter();//2. 释放锁
int savedState = fullyRelease(node);int interruptMode =0;//3. 有可能刚加入条件队列就被转移到同步队列了,如果还在条件队列,就可以放心地挂起自己
while (!isOnSyncQueue(node)){
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node))!=0)
break;}//4. 如果已经转移到同步队列,就尝试获取锁
if (acquireQueued(node, savedState)&& interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter!=null)//5. 清除条件队列中已取消的节点
unlinkCancelledWaiters();
if (interruptMode !=0)
reportInterruptAfterWait(interruptMode);}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
再看一下addConditionWaiter方法,是怎么追加到条件队列末尾的?
// 追加到条件队列末尾
private Node addConditionWaiter(){
Node t = lastWaiter;//1. 清除已取消的节点,找到有效节点
if (t !=null&& t.waitStatus!= Node.CONDITION){
unlinkCancelledWaiters();
t = lastWaiter;}//2. 创建Node节点,状态是-2(表示处于条件队列)
Node node = new Node(Thread.currentThread(), Node.CONDITION);//3. 追加到条件队列末尾
if (t ==null)
firstWaiter = node;
else
t.nextWaiter= node;
lastWaiter = node;
return node;}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
4.4 signal唤醒
signal唤醒的流程:
唤醒条件队列的头节点,并追加到同步队列末尾。
// 唤醒条件队列的头节点
public final void signal(){//1. 只有持有锁的线程才能调用signal方法
if (!isHeldExclusively())
throw new IllegalMonitorStateException();//2. 找到条件队列的头节点
Node first = firstWaiter;
if (first !=null)//3. 开始唤醒
doSignal(first);}// 实际的唤醒方法
private void doSignal(Node first){
do {//4. 从条件队列中移除头节点
if ((firstWaiter = first.nextWaiter)==null)
lastWaiter =null;
first.nextWaiter=null;//5. 使用死循环,一定要转移一个节点到同步队列
} while (!transferForSignal(first)&&(first = firstWaiter)!=null);}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
到底是怎么转移到同步队列末尾的?
// 实际转移方法
final boolean transferForSignal(Node node){//1. 把节点状态从CONDITION改成0
if (!compareAndSetWaitStatus(node, Node.CONDITION,0))
return false;//2. 使用死循环的方式,追加到同步队列末尾(前面已经讲过)
Node p = enq(node);int ws = p.waitStatus;//3. 把前驱节点状态设置SIGNAL(通知他,别忘了唤醒老弟)
if (ws >0||!compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;}