disruptor笔记之七:等待策略,java多线程面试总结

public long waitFor(

final long sequence, Sequence cursor, final Sequence dependentSequence, final SequenceBarrier barrier)

throws AlertException, InterruptedException

{

long availableSequence;

while ((availableSequence = dependentSequence.get()) < sequence)

{

barrier.checkAlert();

ThreadHints.onSpinWait();

}

return availableSequence;

}

@Override

public void signalAllWhenBlocking()

{

}

}

  • 上述代码显示,整个while循环的关键就是ThreadHints.onSpinWait做了什么,源码如下,这里要格外注意,如果ON_SPIN_WAIT_METHOD_HANDLE为空,意味着外面的while循环是个非常消耗CPU的自旋

public static void onSpinWait()

{

if (null != ON_SPIN_WAIT_METHOD_HANDLE)

{

try

{

ON_SPIN_WAIT_METHOD_HANDLE.invokeExact();

}

catch (final Throwable ignore)

{

}

}

}

  • ON_SPIN_WAIT_METHOD_HANDLE为空是很可怕的事情,咱们来看看它是何方神圣?代码还是在ThreadHints.java中,如下所示,真相一目了然,它就是Thread类的onSpinWait方法,如果Thread类没有onSpinWait方法,那么使用BusySpinWaitStrategy作为等待策略就有很高的代价了,环形队列里没有数据时消费线程会执行自旋,很耗费CPU:

static

{

final MethodHandles.Lookup lookup = MethodHandles.lookup();

MethodHandle methodHandle = null;

try

{

methodHandle = lookup.findStatic(Thread.class, “onSpinWait”, methodType(void.class));

}

catch (final Exception ignore)

{

}

ON_SPIN_WAIT_METHOD_HANDLE = methodHandle;

}

  • 好吧,还剩两个问题:Thread类有没有onSpinWait方法还不能确定吗?这个onSpinWait方法是何方神圣?

  • 去看JDK官方文档,如下图,原来这方法是从JDK9才有的,所以对于JDK8使用者来说来说,选用BusySpinWaitStrategy就意味着要面对没做啥事儿的while循环了:

在这里插入图片描述

  • 第二个问题,onSpinWait方法干了些啥?前面的官方文档,以欣宸的英语水平显然是无法理解的,去看stackoverflow吧,如下图,简单的说,就是告诉CPU当前线程处于循环查询的状态,CPU得知后就会调度更多CPU资源给其他线程:

在这里插入图片描述

  • 至此真像大白:环形队列的条件就绪后,BusySpinWaitStrategy策略是通过whlie死循环来做到快速响应的,如果JDK是9或者更高版本,这个死循环带来的CPU损耗由Thread.onSpinWait帮助缓解,如果JDK版本低于9,这里就是个简单的while死循环,至于这种死循环有多消耗CPU,您可以写段简单代码感受一下…

  • 难怪Disruptor源码中会提醒最好是将使用此实例的线程绑定到指定CPU核:

在这里插入图片描述

DummyWaitStrategy

固定返回0,个人觉得这个策略在正常开发中用不上,因为环形队列可用位置始终是0的话,不论是生产还是消费都难以实现:

在这里插入图片描述

LiteBlockingWaitStrategy

  • 看名字,LiteBlockingWaitStrategy是BlockingWaitStrategy策略的轻量级实现,在锁没有竞争的时候(例如独立消费的场景),会省略掉唤醒操作,不过如下图红框所示,作者说他没有充分验证过正确性,因此建议只用于体验,太好了,这个策略我不学了!!!

在这里插入图片描述

TimeoutBlockingWaitStrategy

  • 顾名思义,TimeoutBlockingWaitStrategy表示只等待某段时长,超过了就算超时,其代码和BlockingWaitStrategy类似,只是等待的时候有个时长限制,如下图,一目了然:

在这里插入图片描述

  • 其实我对抛出异常后的处理很感兴趣,去看看吧,外面是熟悉的BatchEventProcessor类,熟悉的processEvents方法,如下图,每次超时异常都交给notifyTimeout处理,而外部的主流程不受影响,依旧不断的从环形队列中等待和获取数据:

在这里插入图片描述

  • 进入notifyTImeout方法,可见实际上是交给成员变量timeoutHandler去处理的,而且处理过程中发生的任何异常都会被捕获,不会抛出去影响外部调用:

在这里插入图片描述

  • 再来看看成员变量是哪来的,如下图,真相大白,咱们开发的EventHandler实现类,如果也实现了Timeouthandler,就被当做成员变量timeoutHandler了:

在这里插入图片描述

  • 至此TimeoutBlockingWaitStrategy也搞清楚了:用于有时间限制的场景,每次等待超时后都会调用业务定制的超时处理逻辑,这个逻辑写到EventHandler实现类中,这个实现类要实现Timeouthandler接口

LiteTimeoutBlockingWaitStrategy

  • LiteTimeoutBlockingWaitStrategy与TimeoutBlockingWaitStrategy的关系,就像BlockingWaitStrategy与LiteBlockingWaitStrategy的关系:作为TimeoutBlockingWaitStrategy的变体,有TimeoutBlockingWaitStrategy的超时处理特性,而且没有锁竞争的时候,省略掉唤醒操作;

  • 作者说LiteBlockingWaitStrategy可用于体验,但正确性并未经过充分验证,但是在LiteTimeoutBlockingWaitStrategy的注释中没有看到这种说法,看样子这是个靠谱的等待策略,可以用,用在有超时处理的需求,而且没有锁竞争的场景(例如独立消费)

SleepingWaitStrategy

  • 和前面几个不同的是,SleepingWaitStrategy没有用到锁,这意味这无需调用signalAllWhenBlocking方法做唤醒处理,相当于省去了生产线程的通知操作,官方源码注释有这么句话引起了我的兴趣,如下图红框,大意是该策略在性能和CPU资源消耗之间取得了平衡,接下来去看看关键代码,来了解这个特性:

在这里插入图片描述

  • 如下图,等到可用数据的过程是个死循环:

在这里插入图片描述

  • 接下来是关键代码了,如下图,可见整个等待过程分为三段:计数器高于100时就只有一个减一的操作(最快响应),计数器在100到0之间时每次都交出CPU执行时间(最省资源),其他时候就睡眠固定时间:

在这里插入图片描述

YieldingWaitStrategy

  • 看过SleepingWaitStrategy之后,再看YieldingWaitStrategy就很容易理解了,和SleepingWaitStrategy相比,YieldingWaitStrategy先做指定次数的自旋,然后不断的交出CPU时间:

在这里插入图片描述

  • 由于在不断的执行Thread.yield()方法,因此该策略虽然很消耗CPU,不过一旦其他线程有CPU需求,很容易从这个线程得到;

PhasedBackoffWaitStrategy

  • 最后是PhasedBackoffWaitStrategy,该策略的特点是将整个等待过程分成下图的四段,四个方块代表一个时间线上的四个阶段:

在这里插入图片描述

  • 这里说明一下上图的四个阶段:
  1. 首先是自旋指定的次数,默认10000次;

  2. 自旋过后,开始带计时的自旋,执行的时长是spinTimeoutNanos的值;

  3. 执行时长达到spinTimeoutNanos的值后,开始执行Thread.yield()交出CPU资源,这个逻辑的执行时长是yieldTimeoutNanos-spinTimeoutNanos;

  4. 执行时长达到yieldTimeoutNanos-spinTimeoutNanos的值后,开始调用fallbackStrategy.waitFor,这个调用没有时间或者次数限制;

  • 现在问题来了fallbackStrategy是何方神圣?PhasedBackoffWaitStrategy类准备了三个静态方法,咱们可以按需选用,让fallbackStrategy是BlockingWaitStrategy、LiteBlockingWaitStrategy、SleepingWaitStrategy这三个中的一个:

public static PhasedBackoffWaitStrategy withLock(

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

我想问下大家当初选择做程序员的初衷是什么?有思考过这个问题吗?高薪?热爱?

既然入了这行就应该知道,这个行业是靠本事吃饭的,你想要拿高薪没有问题,请好好磨练自己的技术,不要抱怨。有的人通过培训可以让自己成长,有些人可以通过自律强大的自学能力成长,如果你两者都不占,还怎么拿高薪?

架构师是很多程序员的职业目标,一个好的架构师是不愁所谓的35岁高龄门槛的,到了那个时候,照样大把的企业挖他。为什么很多人想进阿里巴巴,无非不是福利待遇好以及优质的人脉资源,这对个人职业发展是有非常大帮助的。

如果你也想成为一名好的架构师,那或许这份Java核心架构笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。

中高级开发必知必会:

自己的技术,不要抱怨。有的人通过培训可以让自己成长,有些人可以通过自律强大的自学能力成长,如果你两者都不占,还怎么拿高薪?

架构师是很多程序员的职业目标,一个好的架构师是不愁所谓的35岁高龄门槛的,到了那个时候,照样大把的企业挖他。为什么很多人想进阿里巴巴,无非不是福利待遇好以及优质的人脉资源,这对个人职业发展是有非常大帮助的。

如果你也想成为一名好的架构师,那或许这份Java核心架构笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。

中高级开发必知必会:

[外链图片转存中…(img-3kHYtjiM-1711622941825)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值