0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

强烈反对使用Spring封装的多线程类

PLC工控专栏 来源:加密狗破解 作者:加密狗破解 2022-02-22 08:58 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

在很久很久之前,我有一段痛苦的记忆。那种被故障所驱使的感觉,在我脑海里久久无法驱散。

原因无它,有小伙伴开启了线程池的暴力使用模式。没错,就是下面这篇文章。

夺命故障 ! 炸出了投资人!

我有必要简单的复述一下。其主要原因,就是开发人员,在每一次方法调用里,都创建了一个单独的线程池去处理。这样的话,如果请求量一增加,整个操作系统的压力就会耗尽,最终所有的业务都无法响应。

强烈反对使用Spring封装的多线程类

我一直认为这是一个非常偶发的低级错误,发生频率非常的低。但随着这样的故障越来越多,xjjdog认识到这是一个普遍的现象。

以异步性能优化为目的,反而带来的整体业务不可用的结果,是非常打脸的一种优化。

1.Spring的异步代码

Spring作为Java届的杠把子框架,其过度封装的API深得开发人员的喜爱。根据语义化编程的逻辑,只要某些关键字在语言层面上过得去,我们就可以把它给加上去。比如@Async注解。

我永远想不通是什么给了开发人员勇气,去加上这个@Async注解,因为这种涉及到多线程的东西,即使是自己去创建线程,也是心怀敬畏,唯恐扰了操作系统的安宁。@Async这样的黑盒,真的可以那么顺畅的使用么?

我们不妨debug一下代码,让子弹飞一会儿。

首先,生成一个小小的项目,然后在主类上加上必须的注解。嗯,别忘了这一环,否则你后面加的注解将没什么用处。

@SpringBootApplication
@EnableAsync
publicclassDemoApplication{

复制代码

创造一个带@Async注解的方法。

@Component
publicclassAsyncService{
@Async
publicvoidasync(){
try{
Thread.sleep(1000);
System.out.println(Thread.currentThread());
}catch(Exceptionex){
ex.printStackTrace();
}
}
}

复制代码

然后,做一个对应的test接口,访问时会调用这个async方法。

@ResponseBody
@GetMapping("test")
publicvoidtest(){
service.async();
}

复制代码

访问时,直接打个断点,即可获取执行异步线程的线程池。

强烈反对使用Spring封装的多线程类

可以看到,异步任务使用了一个线程池,它的corePoolSize=8, 阻塞队列采用了无界队列LinkedBlockingQueue。一旦采用了这样的组合,最大线程数就会形同虚设,因为超出8个线程的任务,将全部会被放到无界队列里。使得下面的代码变成了摆设。

thrownewTaskRejectedException("Executor["+executor+"]didnotaccepttask:"+task,var4);

复制代码

如果你的访问量非常大,这些任务将全部堆积在LinkedBlockingQueue里。情况好一点的,这些任务的执行会变得延迟很大;情况坏一点的,任务太多将直接造成内存溢出OOM!

你可能会说,我可以自己指定另外一个ThreadPoolExceute,然后使用@Async注解来声明啊。说这话的同学,一定是能力比较强,或者Review的代码比较少,没有经过猪队友的洗礼。

2.是SpringBoot救了你

SpringBoot是个好东西。

在TaskExecutionAutoConfiguration中,通过生成ThreadPoolTaskExecutor的Bean,来提供默认的Executor。

@ConditionalOnMissingBean({Executor.class})
publicThreadPoolTaskExecutorapplicationTaskExecutor(TaskExecutorBuilderbuilder){
returnbuilder.build();
}

复制代码

也就是我们上面所说的那个。如果没有SpringBoot的助力,Spring将默认使用SimpleAsyncTaskExecutor。

参见org.springframework.aop.interceptor.AsyncExecutionInterceptor。

@Override
@Nullable
protectedExecutorgetDefaultExecutor(@NullableBeanFactorybeanFactory){
ExecutordefaultExecutor=super.getDefaultExecutor(beanFactory);
return(defaultExecutor!=null?defaultExecutor:newSimpleAsyncTaskExecutor());
}

复制代码

这就是Spring大仙所干的事。

SimpleAsyncTaskExecutor类设计的非常操蛋,因为它每执行一次,都会创建一个单独的线程,根本没有共用线程池。比如你的TPS是1000,异步执行了任务,那么你每秒将会生成1000个线程!

这明显是想要累死操作系统的节奏。

protectedvoiddoExecute(Runnabletask){
Threadthread=(this.threadFactory!=null?this.threadFactory.newThread(task):createThread(task));
thread.start();
}

复制代码

3.End

明眼人一看,这种使用new线程的处理方式将会是非常可怕的。但就拿Spring本身来说,引用SimpleAsyncTaskExecutor这个类的地方还不少,包括比较流行的AsyncRestTemplate。

强烈反对使用Spring封装的多线程类

这暴露了很多风险,尤其是竟然在这些列表中看到了redis的身影。这个类的设计,使得任务的执行变的非常的不可控。

看这个API,我感觉Spring是进入了设计的魔怔状态。

这个东西的隐藏bug可能还会更深!比如org.springframework.context.event.EventListener注解,用于实现DDD那套所谓的事件驱动模式,有不少框架直接set了AsyncRestTemplate,那么就等死吧。

赶紧把SimpleAsyncTaskExecutor加入你的API黑名单,或者埋坑清单吧!

创建线程有那么难么?需要使用Spring创建的线程?有时候我实在是想不通,暴露出这样的接口目的是为了什么。

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 封装
    +关注

    关注

    128

    文章

    9398

    浏览量

    149251
  • 多线程
    +关注

    关注

    0

    文章

    279

    浏览量

    21161
  • spring
    +关注

    关注

    0

    文章

    345

    浏览量

    16105
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    逆风C++QT中控台逆向课程

    https://www.bilibili.com/opus/1178756596191199237 QT中控逆向:多线程逻辑与消息处理分析 在软件逆向工程领域,QT框架因其跨平台特性和丰富的功能模块
    的头像 发表于 04-27 10:06 138次阅读

    Java并发编程的“基石”——多线程概念初识

    。Java 的内存可见性机制(如 happens-before 原则)能够确保在极端并发下,任何一个调度节点看到的集群状态都是一致且准确的,从根本上杜绝了“脑裂”和资源超卖。 其次是 精妙的多线程协同
    发表于 04-16 18:50

    【瑞萨RA × Zephyr评测】多线程和看门狗

    本文章旨在评估使用 Zephyr RTOS 在 Renesas FPB-RA6E2 开发板上实现多线程调度与硬件看门狗功能的应用。评估内容包括任务调度、看门狗初始化流程、主程序逻辑的详细解析,以及实验现象与数据分析。
    的头像 发表于 01-10 10:23 2788次阅读
    【瑞萨RA × Zephyr评测】<b class='flag-5'>多线程</b>和看门狗

    解析Linux的进程、线程和协程

    和系统资源。线程的引入使得多核处理器得以充分利用,因为多线程程序可以更有效地分配和管理多核心的计算资源。 线程的特点包括: (1)共享性:线程之间共享同一进程的地址空间,可以更容易
    发表于 12-22 11:00

    多线程的系统

    多线程系统的事件响应也是在中断中完成的,但事件的处理是在线程中完成的。在多线程系统中,线程跟中断一样,也具有优先级,优先级高的线程会被优先执
    发表于 12-08 07:55

    Linux多线程对比单线程的优势

    :「资源利用率」:通过多线程,可以更有效地利用CPU资源,特别是多核CPU。「并行处理」:线程允许同时执行多个任务,提高程序的执行效率。「简化设计」:使用线程可以简化程序设计,因为线程
    发表于 12-01 06:11

    rt-thread studio 如何进行多线程编译?

    ,使用的是5800h+32g内存+sn550 ssd,开启16线程编译时cpu的占用率也只能到30%,编译完整个工程需要3分钟 感觉多线程编译设置没有生效,有办法提高编译速度吗 rtthread studio版本是 2.2.9
    发表于 10-11 09:16

    多线程与多处理有何区别

    多处理也称为进程,进程是一个在自己的内存空间中运行的独立程序。
    的头像 发表于 09-16 14:21 751次阅读

    【HZ-T536开发板免费体验】—— linux创建线程

    自己的私有资源。 在linux系统中,线程状态通常反映了当前线程的当前活动和执行阶段。 主要分为: 1。运行转态 2。阻塞转态 3。终止状态 如何区分单线程多线程? 在单个程序中只
    发表于 09-01 21:31

    【RA4E2开发板评测】LED1及LED2轮流点亮并同时亮8秒,体验FreeRTOS多线程

    我们要多线程操作时,就需要用到RTOS(Real-Time Operating System)。FreeRTOS实时操作系统开发里面使用到vTaskDelay函数,它是对CPU的时分复用,系统将这个
    发表于 08-24 17:24

    Spring拦截器:你的请求休想逃过我的五指山!

    Spring框架中,拦截器(Interceptor)是一种强大的机制,它允许开发者在请求处理的不同阶段插入自定义逻辑。WebApplicationContext作为Spring Web应用的上下文容器,为拦截器的配置和管理提供了基础支持。
    的头像 发表于 07-26 11:25 862次阅读
    <b class='flag-5'>Spring</b>拦截器:你的请求休想逃过我的五指山!

    harmony-utils之LRUCacheUtil,LRUCache缓存工具

    ,借助众多实用工具,致力于助力开发者迅速构建鸿蒙应用。其封装的工具涵盖了APP、设备、屏幕、授权、通知、线程间通信、弹框、吐司、生物认证、用户首选项、拍照、相册、扫码、文件、日志、异常捕获、字符
    的头像 发表于 07-03 18:11 696次阅读

    多线程的安全注意事项

    多线程安全是指多个线程同时访问或修改共享资源时,能够保证程序的正确性和可靠性。 开发者选择TaskPool或Worker进行多线程开发时,在TaskPool和Worker的工作线程中导
    发表于 06-20 07:49

    什么是ArkTS?

    基础库能力示意图 提供异步并发和多线程并发的能力。 支持Promise和async/await等标准的JS异步并发能力。 TaskPool为应用程序提供一个多线程的运行环境,降低整体资源的消耗
    发表于 06-17 06:24

    鸿蒙5开发宝藏案例分享---跨线程性能优化指南

    ;>Worker</span>做多线程开发时,总遇到对象跨线程卡顿的问题,原来鸿蒙早就提供了解决方案。下面结合代码和实战案例,带你彻底玩转性能优化! 一、痛点:跨线程
    发表于 06-12 17:13