创建线程有那么难么?创建线程的大坑要避开

1.spring的异步代码
2.是springboot救了你
3.end
在很久很久之前,我有一段痛苦的记忆。那种被故障所驱使的感觉,在我脑海里久久无法驱散。
原因无它,有小伙伴开启了线程池的暴力使用模式。没错,就是下面这篇文章。
夺命故障 ! 炸出了投资人!
我有必要简单的复述一下。其主要原因,就是开发人员,在每一次方法调用里,都创建了一个单独的线程池去处理。这样的话,如果请求量一增加,整个操作系统的压力就会耗尽,最终所有的业务都无法响应。
我一直认为这是一个非常偶发的低级错误,发生频率非常的低。但随着这样的故障越来越多,xjjdog认识到这是一个普遍的现象。
以异步性能优化为目的,反而带来的整体业务不可用的结果,是非常打脸的一种优化。
1.spring的异步代码
spring作为java届的杠把子框架,其过度封装的api深得开发人员的喜爱。根据语义化编程的逻辑,只要某些关键字在语言层面上过得去,我们就可以把它给加上去。比如@async注解。
我永远想不通是什么给了开发人员勇气,去加上这个@async注解,因为这种涉及到多线程的东西,即使是自己去创建线程,也是心怀敬畏,唯恐扰了操作系统的安宁。@async这样的黑盒,真的可以那么顺畅的使用么?
我们不妨debug一下代码,让子弹飞一会儿。
首先,生成一个小小的项目,然后在主类上加上必须的注解。嗯,别忘了这一环,否则你后面加的注解将没什么用处。
@springbootapplication@enableasyncpublic class demoapplication {  
创造一个带@async注解的方法。
@componentpublic class asyncservice {    @async    public void async(){        try {            thread.sleep(1000);            system.out.println(thread.currentthread());        }catch (exception ex){            ex.printstacktrace();        }    }}  
然后,做一个对应的test接口,访问时会调用这个async方法。
@responsebody@getmapping(test)public void test(){    service.async();}  
访问时,直接打个断点,即可获取执行异步线程的线程池。
可以看到,异步任务使用了一个线程池,它的corepoolsize=8, 阻塞队列采用了无界队列linkedblockingqueue。一旦采用了这样的组合,最大线程数就会形同虚设,因为超出8个线程的任务,将全部会被放到无界队列里。使得下面的代码变成了摆设。
throw new taskrejectedexception(executor [ + executor + ] did not accept task:  + task, var4);  
如果你的访问量非常大,这些任务将全部堆积在linkedblockingqueue里。情况好一点的,这些任务的执行会变得延迟很大;情况坏一点的,任务太多将直接造成内存溢出oom!
你可能会说,我可以自己指定另外一个threadpoolexceute,然后使用@async注解来声明啊。说这话的同学,一定是能力比较强,或者review的代码比较少,没有经过猪队友的洗礼。
2.是springboot救了你
springboot是个好东西。
在taskexecutionautoconfiguration中,通过生成threadpooltaskexecutor的bean,来提供默认的executor。
@conditionalonmissingbean({executor.class})public threadpooltaskexecutor applicationtaskexecutor(taskexecutorbuilder builder) {    return builder.build();}  
也就是我们上面所说的那个。如果没有springboot的助力,spring将默认使用simpleasynctaskexecutor。
参见org.springframework.aop.interceptor.asyncexecutioninterceptor。
@override@nullableprotected executor getdefaultexecutor(@nullable beanfactory beanfactory) { executor defaultexecutor = super.getdefaultexecutor(beanfactory); return (defaultexecutor != null ? defaultexecutor : new simpleasynctaskexecutor());}  
这就是spring大仙所干的事。
simpleasynctaskexecutor类设计的非常操蛋,因为它每执行一次,都会创建一个单独的线程,根本没有共用线程池。比如你的tps是1000,异步执行了任务,那么你每秒将会生成1000个线程!
这明显是想要累死操作系统的节奏。
protected void doexecute(runnable task) { thread thread = (this.threadfactory != null ? this.threadfactory.newthread(task) : createthread(task)); thread.start();}  
3.end
明眼人一看,这种使用new线程的处理方式将会是非常可怕的。但就拿spring本身来说,引用simpleasynctaskexecutor这个类的地方还不少,包括比较流行的asyncresttemplate。
这暴露了很多风险,尤其是竟然在这些列表中看到了redis的身影。这个类的设计,使得任务的执行变的非常的不可控。
看这个api,我感觉spring是进入了设计的魔怔状态。
这个东西的隐藏bug可能还会更深!比如org.springframework.context.event.eventlistener注解,用于实现ddd那套所谓的事件驱动模式,有不少框架直接set了simpleasynctaskexecutor,那么就等死吧。
赶紧把simpleasynctaskexecutor加入你的api黑名单,或者埋坑清单吧!
创建线程有那么难么?需要使用spring创建的线程?有时候我实在是想不通,暴露出这样的接口目的是为了什么。
就连原生的线程池我们还没搞明白呢,你还给包了一层,这是方便我们甩锅啊!

石墨烯研发接连突破 未来或将成为理想产氢平台
ARM:从未断供华为 与海思会保持长期合作
被英特尔抢代工订单 台积电与富士通联盟共抗敌
雷达技术的进步和舱内传感的发展
调光台灯的设计
创建线程有那么难么?创建线程的大坑要避开
华润微可提供全套功率器件解决方案 满足细分应用的需求
深入解读 PaddlePaddle EDL
纳微NV6134A助力小米13 Ultra澎湃秒充
叶甜春说:IC制造业最核心的竞争力还是IC装备制造
哪种PC品类更值得买
智能锁的种类有哪些,它们的用途是什么
CMOS RF技术迎来新一轮发展
拉索生物:从下游产品端企业向全产业链布局,核心能力是技术
什么是配线架?为什么要使用配线架?
无人机飞越60英里送邮件
瑞科慧联完成千万Pre-A轮,智能家居布局将全面开启
联发科前COO真诚谈从事芯片行业20多年以来的成功案例与失败经验
IMX6 L3035编译手册
国外新闻 Siri创始人称苹果没开放语音助手是错误 特斯拉需要职业经理人