JUC源码分析-Reetrantlock-Condition

概述

Condition是Reerantlock 实现了 wait- notify 模式的类

synchronized锁 wait- notify 模式 实现的 是 Object.wait(),Object.notify()。

so 重入锁 Reerantlock 也要有wait- notify 模式 的实现,他就是 Condition。

执行 await 和signal 的前后必须有Reerantlock.lock()和Reerantlock.unlock,否则或抛出IllegalMonitorStateException异常。

 

Condition在ArrayBlockingQueue和 LinkedBlockingQueue中有重要应用。实现存数据和取数据方法之间的相互等待和唤醒。

 

Reerantlock中创建 Condition的方法

 

public Condition newCondition() {

return sync.newCondition();

}

final ConditionObject newCondition() {

return new ConditionObject();

}

跟踪发现 ConditionObject是 AbstractQueuedSynchronizer 的内部类。

 

 

源码分析

await

await 和signal交互逻辑

ConditionObject中维护一个新的 waiter队列,执行await后创建新节点入队,然后释放锁,while循环检查自己是否在锁等待队列中,没有则进入阻塞。signal 按照FIFO的顺序从waiter队列中取出结点 ,入队锁等待队列。然后 signal 所在的线程锁释放后,await 阻塞被释放,执行后续处理。

public final void await() throws InterruptedException {

 

if (Thread.interrupted())

throw new InterruptedException();

Node node = addConditionWaiter();//入队 waiter队列

int savedState = fullyRelease(node);//释放当前lock,执行await 需要释放锁。

 

int interruptMode = 0;

while (!isOnSyncQueue(node)) {//检查 当前节点是否在等待队列中

LockSupport.park(this);//没在 进入阻塞

if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)

break;

}

if (acquireQueued(node, savedState) && interruptMode != THROW_IE)

interruptMode = REINTERRUPT;

if (node.nextWaiter != null) // clean up if cancelled

unlinkCancelledWaiters();//移除取消的节点

if (interruptMode != 0)

reportInterruptAfterWait(interruptMode);

}

 

await执行分析: 初始化 waiter队列,新创建节点 ,并入队,然后释放锁,检查自己是否在锁等待队列中,在队列中就执行park阻塞,不在队列中证明 singal已经将 刚才入队的节点 取出放入了等待队列,无需执行park阻塞,执行acquireQueued()根据节点状态获取锁成功后,如果阻塞过程中执行了打断,抛出异常,或记录打断状态。

 

private Node addConditionWaiter() {

Node t = lastWaiter;

// If lastWaiter is cancelled, clean out.

if (t != null && t.waitStatus != Node.CONDITION) {

unlinkCancelledWaiters();//从头到尾移除被取消的节点

t = lastWaiter;

}

Node node = new Node(Thread.currentThread(), Node.CONDITION);

if (t == null)//如果尾部是null,初始化节点

firstWaiter = node;

else//将节点 挂到尾部

t.nextWaiter = node;

lastWaiter = node;

return node;

}

private int checkInterruptWhileWaiting(Node node) {

return Thread.interrupted() ?

(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :

0;

}

在await过程中执行了打断,如果是在signal前执行,说明 await在执行LockSupport.park(this);中被打断,需要抛出异常,如果是在signal后执行,打断没有起作用要重新打断。

 

//将取消条件等待的结点从条件队列转移到同步队列中

final boolean transferAfterCancelledWait(Node node) {

//如果这步CAS操作成功的话就表明中断发生在signal方法之前

if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {

//状态修改成功后就将该结点放入同步队列尾部

enq(node);//上面的cas操作执行后,再执行signal 就会跳过这个node。

return true;

}

//到这里表明CAS操作失败, 说明中断发生在signal方法之后

while (!isOnSyncQueue(node)) {

//如果sinal方法还没有将结点转移到同步队列, 就通过自旋等待一下

Thread.yield();

}

return false;

}

 

signal

public final void signal() {

if (!isHeldExclusively())//检查是否持有互斥锁,没有就抛出异常

throw new IllegalMonitorStateException();

Node first = firstWaiter;

if (first != null)

doSignal(first);

}

 

 

private void doSignal(Node first) {

do {//从 firstWaiter 开始向后 出队节点

if ( (firstWaiter = first.nextWaiter) == null)

lastWaiter = null;//全部出队完成,更新lastWaiter

first.nextWaiter = null;

} while (!transferForSignal(first) &&(first = firstWaiter) != null);

//将waiter队列中的node转移到 锁等待队列中,一直循环直到成功一次或waiter队列空了

}

doSignal 就是将waiter队列中的node转移到 锁等待队列中,一致循环直到成功一次或waiter队列空了。

final boolean transferForSignal(Node node) {

//如果成功,可以继续执行,否则证明 这个node被取消了

if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))

return false;

 

Node p = enq(node);//入队锁等待队列,await的阻塞 等待锁释放后被唤醒

int ws = p.waitStatus;

if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))

LockSupport.unpark(node.thread);

return true;

}

doSignalAll

private void doSignalAll(Node first) {

lastWaiter = firstWaiter = null;// 移除 共享变量对队列的关联,为 出队 waiter队列中全部节点做准备。

do {

//获取下个节点 为后续循环获取节点最准备

Node next = first.nextWaiter;

//unlink first

first.nextWaiter = null;

transferForSignal(first);//转移first到锁等待队列中

first = next;//first 出队,first指向next节点

} while (first != null);

}

doSignalAll 要点分析:

doSignalAll 是将waiter中的node全部转移到等待队列中,如果多个线程已经执行了await. 执行

doSignalAll 后,await会按照 waiter队列中入队的顺序获得锁,排队执行。

注意链表中节点被出队的逻辑 先unlink node from queue, 命名一个变量指向这个node,使用结束后,这个变量指向null。然后这个节点才会被GC掉。 GC的条件:没有任何一个对象和变量名能访问到这个对象实例。在队列中 node被回收的条件:队列中节点 没有任何个对象和变量名能访问到这个node.

 

疑问分析

Condition 的await和signal是怎样抛出 java.lang.IllegalMonitorStateException的?

Reerantlock 的lock的方法 在获取锁后会save互斥锁持有线程,就是执行lock的当前线程。

signal()执行前会检查这个变量,抛出IllegalMonitorStateException。

await() 执行过程中需要释放锁,释放锁的方法中会检查这个变量,抛出IllegalMonitorStateException

 

Reerantlock-lock

final void lock() {

if (compareAndSetState(0, 1))

setExclusiveOwnerThread(Thread.currentThread());

else

acquire(1);

}

 

先看一下signal中的 IllegalMonitorStateException检查

public final void signal() {

if (!isHeldExclusively())

throw new IllegalMonitorStateException();

//...

}

protected final boolean isHeldExclusively() {

return getExclusiveOwnerThread() == Thread.currentThread();

}

再看一下await中的 IllegalMonitorStateException检查

public final void await() throws InterruptedException {

//...

int savedState = fullyRelease(node);

//...

}

 

final int fullyRelease(Node node) {

//...

if (release(savedState))

//...

}

public final boolean release(int arg) {

if (tryRelease(arg)) {//调用的是Reerantlock 的方法

Node h = head;

if (h != null && h.waitStatus != 0)

unparkSuccessor(h);

return true;

}

return false;

}

Reerantlock 释放方法检测IllegalMonitorStateException

protected final boolean tryRelease(int releases) {

//...

if (Thread.currentThread() != getExclusiveOwnerThread())

throw new IllegalMonitorStateException();

//...

}

 

说明:ConditionObject是AbstractQueuedSynchronize的 内部类,Reerantlock 继承了AbstractQueuedSynchronize ,执行new ConditionObject() 实际上是创建了Reerantlock 内部类 ConditionObject的实例,so 这里 访问tryRelease是 Reerantlock 的实现

 

总结:Condition提供了 wait-notify机制的API,其实现类ConditionObject基于基于Reetrantlock实现。ConditionObject 本身自维护一个新的等待队列,执行await时创建节点链接到这个队列尾部,进入阻塞,执行signal时从队列头部 取出一个节点放入 Reetrantlock 的锁等待队列。执行Reetrantlock.unlock释放 await中被阻塞的线程,await被释放后 通过 Reetrantlock 的acquireQueue 排队获取锁。

整个Condition功能的实现 简单的概括: await在新队列中加入节点,signal从新队列中取出节点放入AQS的等待队列,借助AQS的加锁,释放,acquireQueue 排队获取锁 实现了 wait-notify机制。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值