多线程到底用不用在业务代码上???

简介: 在当今的软件开发中,多线程技术是一种常见的优化方式,可以显著提高程序的性能和响应能力。然而,对于业务代码是否应该使用多线程,不同的开发者和专家可能会有不同的看法和经验。在这篇文章中,我们将探讨多线程在业务代码中的应用,并分析其利弊。综上所述,是否在业务代码中使用多线程需要根据具体情况来决定。如果业务系统需要同时处理多个任务,并且每个任务都可以独立地执行,那么使用多线程可以提高系统的性能和响应能力。然而,如果业务逻辑比较简单,或者系统的设计不允许使用多线程,那么使用单线程可能更加简单和安全。

 其他系列文章导航

Java基础合集

数据结构与算法合集

设计模式合集

多线程合集

分布式合集

ES合集


文章目录

其他系列文章导航

文章目录

前言

一、dubbo场景下的多线程

二、适合用线程池的场景

2.1 多个模块收集信息场景

2.2 定时任务触发调用场景

三、总结


前言

在当今的软件开发中,多线程技术是一种常见的优化方式,可以显著提高程序的性能和响应能力。

然而,对于业务代码是否应该使用多线程,不同的开发者和专家可能会有不同的看法和经验。

在这篇文章中,我们将探讨多线程在业务代码中的应用,并分析其利弊。


一、dubbo场景下的多线程

先来讲讲dubbo场景,整个调用链路非常的清晰:

image.gif编辑

来,请你告诉我这里面有线程池吗?

没有!

是的,在日常的开发中,我就是写个接口给别人调用嘛,在我的接口里面并没有线程池相关的代码,只有 CRUD 相关的业务代码。

同时,在日常的开发中,我也经常调用别人提供给我的接口,也是一把梭,撸到底,根本就不会用到线程池。

所以,站在我,一个开发人员的角度,这个里面没有线程池。

但是,当我们换个角度,再看看,它也是可以有的

比如这样:

image.gif编辑

反应过来没有?

我们发起一个 HTTP 调用,是由一个 web 容器来处理这个请求的,你甭管它是 Tomcat,还是 Jetty、Netty、Undertow 这些玩意,反正是个 web 容器在处理。

那你说,这个里面有线程池吗?

是有的!!

比如说Tomcat,虽然不是你写的,但是你确实用了。

会由 web 容器中的一个线程来进行调用。所以,站在 web 容器的角度,这里是有一个线程池的:

image.gif编辑

同理,在 RPC 框架中,不管是消费方,还是服务提供方,也都存在着线程池

本篇文章侧重不在dubbo,我就不多说dubbo的线程池了。

我们主要关注这个业务线程池。

反正站在 Dubbo 框架的角度,又可以补充一下这个图片了:

image.gif编辑

那么问题来了,在当前的这个情况下?

当有人反馈:哎呀,这个服务吞吐量怎么上不去啊?

你怎么办?你会 duang 的一下在业务逻辑里面加一个线程池吗?

image.gif编辑

大哥,前面有个 web 容器的线程池,后面有个框架的线程池,两头不调整,你在中间加个线程池,加它有啥用啊?对吧。

可以对web容器进行调优,然后对dubbo框架也可以进行调优

甚至,你在业务代码中加入一个线程池之后,反而会被“反噬”。

比如,你 duang 的一下怼个线程池在这里,我们先只看 web 容器和业务代码对应的部分:

image.gif编辑

由于你的业务代码中有线程池的存在,所以当接受到一个 web 请求之后,立马就把请求转发到了业务线程池中,由线程池中的线程来处理本次请求,从而释放了 web 请求对应的线程,该线程又可以里面去处理其他请求。

这样来看,你的吞吐量确实上去了。

在前端来看,非常的 nice,请求立马得到了响应。

但是,你考虑过下游吗?

你的吞吐量上涨了,下游同一时间处理的请求就变多了。如果下游跟不上处理,顶不住了,直接就是崩给你看怎么办?

image.gif编辑

而且下游不只是你一个调用方,由于你调用的太猛,导致其他调用方的请求响应不过来,是会引起连锁反应的。

所以,这种场景下,为了异步怼个线程池放着,我觉得还不如用消息队列来实现异步化,顶天了也就是消息堆积嘛,总比服务崩了好,这样更加稳妥。

或者至少和下游勾兑一下,问问我们这边吞吐量上升,你们扛得住不。

image.gif编辑

我们最开始的案例是想要在业务逻辑中增加一个线程池,对着一个下游服务就是一顿猛攻,不是所谓的串行改并行,而是用更多的线程,带来更多的串行


二、适合用线程池的场景

2.1 多个模块收集信息场景

比如一个请求要经过若干个服务获取数据,且这些数据没有先后依赖,最终需要把这些数据组合起来,一并返回,这样经典的场景:

image.gif编辑

用户点商品详情,你要等半天才展示给用户,那用户肯定骂骂咧咧的久走了。

这个时候,八股文上是怎么说的:用线程池来把串行的动作改成并行

image.gif编辑

这个场景也是增加了服务 A 的吞吐量,但是用线程池就是非常正确的,没有任何毛病。

2.2 定时任务触发调用场景

比如你有一个定时任务,要从数据库中捞出状态为初始化的数据,然后去调用另外一个服务的接口查询数据的最终状态。

image.gif编辑

如果你的业务代码是这样的:

//获取订单状态为初始化的数据(0:初始化 1:处理中 2:成功 3:失败)
//select * from order where order_status=0;
ArrayList initOrderInfoList = queryInitOrderInfoList();
//循环处理这批数据
for(OrderInfo orderInfo : initOrderInfoList){
    //捕获异常以免一条数据错误导致循环结束
    try{
        //发起rpc调用
        String orderStatus = queryOrderStatus(orderInfo.getOrderId);
        //更新订单状态
        updateOrderInfo(orderInfo.getOrderId,orderStatus);  
    } catch (Exception e){
        //打印异常
    }
}

image.gif

虽然你框架中使用了线程池,但是你就是在一个 for 循环中不停的去调用下游服务查询数据状态,是一条数据一条数据的进行处理,所以其实同一时间,只是使用了框架的线程池中的一个线程。

为了更加快速的处理完这批数据,这个时候,你就可以怼一个线程池放在 for 循环里面了:

//循环处理这批数据
for(OrderInfo orderInfo : initOrderInfoList){
    //使用线程池
    executor.execute(() -> {
        //捕获异常以免一条数据错误导致循环结束
        try {
            //发起rpc调用
            String orderStatus = queryOrderStatus(orderInfo.getOrderId);
            //更新订单状态
            updateOrderInfo(orderInfo.getOrderId, orderStatus);
        } catch (Exception e) {
            //打印异常
        }
    });
}

image.gif

image.gif编辑

需要注意的是,这个线程池的参数怎么去合理的设置,是需要考虑的事情。

同时这个线程池的定位,就类似于 web 容器线程池的定位。

或者这样对比起来看更加清晰一点:

image.gif编辑

定时任务触发的时候,在发起远程接口调用之前,没有线程池,所以我们可以启用一个线程池来加快数据的处理。

而 Http 调用或者 RPC 调用,框架中本来就已经有一个线程池了,而且也给你提供了对应的性能调优参数配置,那么首先考虑的应该是把这个线程池充分利用起来。

如果仅仅是因为异步化之后可以提升服务响应速度,没有达到串行改并行的效果,那么我更加建议使用消息队列。


三、总结

综上所述,是否在业务代码中使用多线程需要根据具体情况来决定。

如果业务系统需要同时处理多个任务,并且每个任务都可以独立地执行,那么使用多线程可以提高系统的性能和响应能力。

然而,如果业务逻辑比较简单,或者系统的设计不允许使用多线程,那么使用单线程可能更加简单和安全。

在决定是否使用多线程时,需要考虑系统的需求、资源和设计等因素。



目录
相关文章
|
2月前
多线程案例-定时器(附完整代码)
多线程案例-定时器(附完整代码)
262 0
|
2月前
|
数据采集 Python
【Python自动化】多线程BFS站点结构爬虫代码,支持中断恢复,带注释
【Python自动化】多线程BFS站点结构爬虫代码,支持中断恢复,带注释
46 0
|
2月前
|
Java
Java使用线程池代码
Java使用线程池代码
47 0
|
2月前
|
Java
|
6天前
|
安全 Python
告别低效编程!Python线程与进程并发技术详解,让你的代码飞起来!
【7月更文挑战第9天】Python并发编程提升效率:**理解并发与并行,线程借助`threading`模块处理IO密集型任务,受限于GIL;进程用`multiprocessing`实现并行,绕过GIL限制。示例展示线程和进程创建及同步。选择合适模型,注意线程安全,利用多核,优化性能,实现高效并发编程。
20 3
|
2月前
|
设计模式 监控 Java
Java多线程基础-11:工厂模式及代码案例之线程池(一)
本文介绍了Java并发框架中的线程池工具,特别是`java.util.concurrent`包中的`Executors`和`ThreadPoolExecutor`类。线程池通过预先创建并管理一组线程,可以提高多线程任务的效率和响应速度,减少线程创建和销毁的开销。
47 2
|
2月前
|
安全 Java
Java多线程基础-10:代码案例之定时器(一)
`Timer` 是 Java 中的一个定时器类,用于在指定延迟后执行指定的任务。它常用于实现定时任务,例如在网络通信中设置超时或定期清理数据。`Timer` 的核心方法是 `schedule()`,它可以安排任务在延迟一段时间后执行。`
60 1
|
26天前
|
Java
【代码诗人】Java线程的生与死:一首关于生命周期的赞歌!
【6月更文挑战第19天】Java线程生命周期,如诗般描绘了从新建到死亡的旅程:创建后待命,`start()`使其就绪,获得CPU则运行,等待资源则阻塞,任务完或中断即死亡。理解生命周期,善用锁、线程池,优雅处理异常,确保程序高效稳定。线程管理,既是艺术,也是技术。
|
28天前
|
API
linux---线程互斥锁总结及代码实现
linux---线程互斥锁总结及代码实现
|
26天前
|
Java
【代码诗人】Java线程的生与死:一首关于生命周期的赞歌!
【6月更文挑战第19天】在Java中,线程经历新建、就绪、运行、阻塞和死亡5个阶段。通过`start()`从新建转为就绪,进而可能运行;阻塞可能因等待资源;完成任务或中断后死亡。管理线程生命周期涉及合理使用锁、线程池、异常处理和优雅关闭,如使用`volatile`和中断标志。了解这些,能提升程序效率和稳定性。