phaser java_java并发编程:Phaser同步工具原理

Phaser是java7版本添加的一个同步工具,相比CyclicBarrier、CountDownLatch、Semaphore等同步工具,Phaser使用更灵活而且可复用(CyclicBarrier也可复用),Phaser包含几个动作:

register:给Phaser增加parties,并且可以通过deRegister减少总parties(CyclicBarrier、CountDownLatch、Semaphore等工具不具备这种灵活性)。

arrive:parties已到达。

awaitAdvance:在所有parties都到达之前当前线程处于挂起等待状态,当所有parties都已到达之后线程被唤醒并且Phaser年龄增加,未到达parties数还原,Phaser复用。

Phaser通过status字段来实现同步逻辑,status是一个64位的long变量,它有包含了四个维度的语义:

1、第0-15位,当前未到达的parties,调用arriveXXX时,该值-1,调用register时+1;

2、第16-31位,当前总parties,调用register时+1,deRegister时-1;

3、第32-62位,phase,即Phaser的年龄,当未到达的parties减到0(即所有parties已到达)时,phase自动加1,并且把16-31位的parties数复制到0-15位,从而该Phaser可以继续复用;

51c9565d6a31d0aa0555105e18c9d911.png

当Phaser的parties数比较大的高并发场景下,Phaser的status变量的竞争会非常激烈,register、arrive等操作发起的CAS操作预测将会大概率失败导致大量CAS操作被重复调用,增加CPU开销。可以通过构造Phaser分层树的方式来分离竞争,子Phaser第一次register时,把该子Phaser注册到父Phaser,当子Phaser所有parties都已经arrive时,把它从父Phaser中反注册。

1b04601d8e3673d8ed81c909930a4c2b.png

当根Phaser的所有子Phaser的parties都已经arrive时,整个Phaser树升级phase递增,通过这种方式,所有的arrive、register操作在子Phaser进行就可以,根Phaser只需负责Phaser的升级,这样可以把部分对status的访问修改分离到子Phaser中,通过分散竞争点提高Phaser的吞吐量。

3164ce8294577d2dfe2c968ee3da5177.png

下面示例代码就把9个parties分散到了两个子Phaser中:

a6c98e080ee56d6b62300804beede819.png

注册parties时,主要做的事情就是修改status变量的parties部分和unarrive部分,同时也可以看到,在第一注册时,调用了代码parent.doRegister(1),注册一次party到父Phaser。

线程触发arrive操作时,如果当前Phaser的所有parties都已经arrive,那么调用一次parent.doArrive(1),如果所有parties都已经arrive并且当前Phaser时根Phaser,说明该Phaser可以升级,phase值加1,唤醒由于调用了awaitXXX被阻塞的线程。在升级时有个onAdvance回调可以让调用者终止Phaser。

触发Phaser的awaitAdvance时,Phaser的await操作不会直接挂起线程,会先对根Phaser的status自旋检查,检查phase是否发生了变化,自旋了若干次(这个数值跟当前CPU的核心数有关)之后如果phase还未发生变化则挂起线程,这样做的目的是挂起线程会造成上下文切换,如果Phaser在很短的时间内就升级了,那么这样就减少了上下文切换次数提高CPU吞吐量,但是自旋检查也会造成CPU消耗,所以也不能一直自旋。在上面的arrive方法中可以看到,当所有parties都arrive之后修改phase值加1,所以internalAwaitAdvance方法中的while条件将不成立,跳出循环唤醒所有等待的线程。下面是唤醒线程的方法:

7f38c31b1e3abc1698cc370c1f510f2b.png

这个方法相对简单,就是有一点需要注意的是:有两个线程等待队列头节点分别是evenQ和oddQ,这是因为在并发场景下,老Phaser所有parties都已经arrive之后等待队列的线程正在被唤醒,但是此时又有线程在对升级后的Phaser调用了await,如果只有一个队列的话那么此时队列头结点出现激烈的竞争,所以这里面把相邻的年龄的Phaser等待线程放在两个队列中可以达到分离竞争的目的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值