千家信息网

反对使用Spring封装的多线程类原因是什么

发表于:2025-01-16 作者:千家信息网编辑
千家信息网最后更新 2025年01月16日,本文小编为大家详细介绍"反对使用Spring封装的多线程类原因是什么",内容详细,步骤清晰,细节处理妥当,希望这篇"反对使用Spring封装的多线程类原因是什么"文章能帮助大家解决疑惑,下面跟着小编的
千家信息网最后更新 2025年01月16日反对使用Spring封装的多线程类原因是什么

本文小编为大家详细介绍"反对使用Spring封装的多线程类原因是什么",内容详细,步骤清晰,细节处理妥当,希望这篇"反对使用Spring封装的多线程类原因是什么"文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

前言:

工作总难免会遇到被故障所驱使,其实是开启了线程池的暴力使用模式

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

我一直认为这是一个非常偶发的低级错误,发生频率非常的低。但随着这样的故障越来越多,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了AsyncRestTemplate,那么就等死吧。

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

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

读到这里,这篇"反对使用Spring封装的多线程类原因是什么"文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注行业资讯频道。

线程 任务 注解 原因 封装 代码 操作系统 东西 人员 文章 方法 系统 队列 处理 生成 设计 业务 内容 就是 情况 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 中国知网数据库检索电脑版 深圳市蓝思网络技术有限 政府招聘网络安全人员 维护网络安全从我做起留言 武隆区工商软件开发服务特点 工业工程与数据库有关吗 登录框数据库连接失败 数据库考勤签到表怎么设计 数据文件是数据库中不可细分的 吴忠网络技术是什么 超算服务器维修站点地址 有扫码枪怎么建数据库 四川省互联网新科技 疫情 网络技术 坪山区网络技术转移优点 服务器无法响应状态码 服务器关机了还能访问吗 我的世界混乱服务器生存第一集 国服英雄联盟游戏加速服务器代理 windows服务器分配账号 临沂网络安全教育培训平台 对网络安全的实验总结 广州培训软件开发团队 软件开发就是编写程序对吗 jump代理服务器原理 无法建立与该服务器安全连接 互联网电视服务器密码 贵州省服务器自动生产线 一点资讯知微数据库 查询orcale数据库版本
0