继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Java并发之Condition的实现分析

慕虎7371278
关注TA
已关注
手记 1259
粉丝 203
获赞 873

一、Condition的概念

介绍

回忆 synchronized 关键字,它配合 Object 的 wait()、notify() 系列方法可以实现等待/通知模式。

对于 Lock,通过 Condition 也可以实现等待/通知模式。

Condition 是一个接口。

Condition 接口的实现类是 Lock(AQS)中的 ConditionObject。

Lock 接口中有个 newCondition() 方法,通过这个方法可以获得 Condition 对象(其实就是 ConditionObject)。

因此,通过 Lock 对象可以获得 Condition 对象。

Locklock=newReentrantLock();Condition c1 = lock.newCondition();Condition c2 = lock.newCondition();

二、Condition的实现分析

实现

ConditionObject 类是 AQS 的内部类,实现了 Condition 接口。

publicclassConditionObjectimplementsCondition,java.io.Serializable{privatetransientNode firstWaiter;privatetransientNode lastWaiter;        ...

可以看到,等待队列和同步队列一样,使用的都是同步器 AQS 中的节点类 Node。

同样拥有首节点和尾节点,

每个 Condition 对象都包含着一个 FIFO 队列。

结构图:

webp

等待

调用 Condition 的 await() 方法会使线程进入等待队列,并释放锁,线程状态变为等待状态。

publicfinalvoidawait()throwsInterruptedException{if(Thread.interrupted())thrownewInterruptedException();    Node node = addConditionWaiter();//释放同步状态(锁)intsavedState = fullyRelease(node);intinterruptMode =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 cancelledunlinkCancelledWaiters();if(interruptMode !=0)                reportInterruptAfterWait(interruptMode);}

分析上述方法的大概过程:

将当前线程创建为节点,加入等待队列;

释放锁,唤醒同步队列中的后继节点;

while循环判断节点是否放入同步队列:

没有放入,则阻塞,继续 while 循环(如果已经中断了,则退出)

放入,则退出 while 循环,执行后面的判断

退出 while 说明节点已经在同步队列中,调用 acquireQueued() 方法加入同步状态竞争。

竞争到锁后从 await() 方法返回,即退出该方法。

webp

addConditionWaiter() 方法:

privateNodeaddConditionWaiter(){    Node t = lastWaiter;if(t !=null&& t.waitStatus != Node.CONDITION) {//清除条件队列中所有状态不为Condition的节点unlinkCancelledWaiters();        t = lastWaiter;    }//将该线程创建节点,放入等待队列Node node =newNode(Thread.currentThread(), Node.CONDITION);if(t ==null)        firstWaiter = node;elset.nextWaiter = node;    lastWaiter = node;returnnode;}

过程分析:同步队列的首节点移动到等待队列。加入尾节点之前会清除所有状态不为 Condition 的节点。

通知

调用 Condition 的 signal() 方法,可以唤醒等待队列的首节点(等待时间最长),唤醒之前会将该节点移动到同步队列中。

publicfinalvoidsignal(){//判断是否获取了锁if(!isHeldExclusively())thrownewIllegalMonitorStateException();    Node first = firstWaiter;if(first !=null)        doSignal(first);}

过程:

先判断当前线程是否获取了锁;

然后对首节点调用 doSignal() 方法。

privatevoiddoSignal(Node first){do{if( (firstWaiter = first.nextWaiter) ==null)            lastWaiter =null;        first.nextWaiter =null;    }while(!transferForSignal(first) &&       (first = firstWaiter) !=null);}

过程:

修改首节点;

调用 transferForSignal() 方法将节点移动到同步队列。

finalbooleantransferForSignal(Node node){//将节点状态变为0   if(!compareAndSetWaitStatus(node, Node.CONDITION,0))returnfalse;//将该节点加入同步队列Node p = enq(node);intws = p.waitStatus;//如果结点p的状态为cancel 或者修改waitStatus失败,则直接唤醒if(ws >0|| !compareAndSetWaitStatus(p, ws, Node.SIGNAL))        LockSupport.unpark(node.thread);returntrue;}

调用同步器的 enq 方法,将节点移动到同步队列,

满足条件后使用 LockSupport 唤醒该线程。

webp

当 Condition 调用 signalAll() 方法:

publicfinalvoidsignalAll() {if(!isHeldExclusively())thrownewIllegalMonitorStateException();    Node first = firstWaiter;if(first !=null)        doSignalAll(first);}privatevoiddoSignalAll(Node first) {    lastWaiter = firstWaiter =null;do{        Nodenext= first.nextWaiter;        first.nextWaiter =null;        transferForSignal(first);        first =next;    }while(first !=null);}

可以看到 doSignalAll() 方法使用了 do-while 循环来唤醒每一个等待队列中的节点,直到 first 为 null 时,停止循环。

一句话总结 signalAll() 的作用:将等待队列中的全部节点移动到同步队列中,并唤醒每个节点的线程。



作者:Java大生
链接:https://www.jianshu.com/p/c1232b09e35f


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP