Java项目中到底该怎么使用线程池?
一. 前言
最近有不少小伙伴私聊老师,问我能不能讲讲项目中怎么使用线程池?所以,今天老师就给大家安排一下,满足粉丝们的要求,给大家讲一下之前我在项目中使用线程池的一些经验。
二. 线程池简介
我们想要在项目中使用线程池,就必须要知道线程池是啥、咋用?所以请各位先跟着老师学学关于线程池的基础知识吧。
2.1 线程池
我们先来看看《阿里巴巴Java开发规范》中对于线程池的使用规范,其中有如下要求:
我们平时开发使用时,一般都是使用Java原生的线程池API,也就是ThreadPoolExecutor来创建线程。我们利用该API进行统一的线程管理,在使用时只需要调用添加任务即可。
2.2 代码演示
接下来就是一段线程池的使用代码演示,大家可以参考一下。
@Test
public void thread(){
//池化技术 :1.复用率 2.有效控制 3.性能
//原生线程池 7个参数
/**
* 使用原生线程池创建
* 7个参数
* 1.核心线程数
* 2.最大线程数
* 3.空闲时间
* 4.空闲时间的时间单位
* 5.阻塞队列 7种
* 6.线程工厂
* 7.拒绝策略 4种
* 线程池工作原理:先核心线程-->再阻塞队列-->最大线程数-->拒绝策略*/
ThreadPoolExecutor poolExecutor=new ThreadPoolExecutor(4,10,
10, TimeUnit.SECONDS,new ArrayBlockingQueue<>(10),
Executors.defaultThreadFactory() ,
new ThreadPoolExecutor.AbortPolicy());
//设置是否回收核心线程
poolExecutor.allowCoreThreadTimeOut(true);
//线程池的线程预热
poolExecutor.prestartAllCoreThreads();
//添加任务
poolExecutor.execute(()->{
System.err.println("遇见老师说:线程池在处理任务");
});
}
2.3 线程池的工作过程
那么上述代码中的线程池,是怎么处理线程任务的呢?且来听老师给你唠唠,线程池接收到一个线程任务后,会经过如下步骤:
如果发现线程池中有空闲线程,则直接执行该任务;
如果没有空闲线程,且当前运行的线程数少于corePoolSize,则创建新的线程执行该任务;
如果没有空闲线程,且当前的线程数等于corePoolSize,同时阻塞队列未满,则将任务入队列,而不添加新的线程;
如果没有空闲线程,且阻塞队列已满,同时池中的线程数小于maximumPoolSize ,则创建新的线程执行任务;
如果没有空闲线程,且阻塞队列已满,同时池中的线程数等于maximumPoolSize,则根据构造函数中的 handler 指定的策略来拒绝新的任务。
以上就是关于线程池的一些基本常识。
三. 项目中使用线程池
言归正传,咱们再来说一下在项目中到底该如何使用线程池。老师之前给某个公司做过一个项目,其中有个业务,要满足大批量Excel格式的数据上传并导入到数据库中。老师本以为只是几百条数据,结果在对接时才发现,对方的数据量都是上万级别的批量数据导入。
来吧,直接让你看看老师的实现代码。
@Override
public R uploadExcel(MultipartFile file) throws IOException {
//1.获取上传的内容 2.解析文件 3.线程池批量新增
//1.验证上传文件是否非空
if(!file.isEmpty()){
//2.获取上传内容
InputStream is=file.getInputStream();
//3.解析Excel
EasyExcel.read(is,Department.class,new PageReadListener
((list)->{
//使用线程池,保证每个线程处理的数据量不超过1000条
//每个线程处理1000条数据,然后根据数据进行分片,每次计算每个任务完成的数据量的范围
int i=0;
int j=list.size()%1000==0?list.size()/1000:list.size()/1000+1;
//循环添加任务到线程池中
for(int m=1;m<=j;m++){
ArrayList l=new ArrayList();
l.addAll(list.subList((m-1)*1000,m*1000));
ThreadPoolUtil.getInstance().poolExecutor.execute(()->{
//4.调用 dao 实现批量新增
dao.addBatch(l);
});
}
})).sheet().doRead();
}
return R.ok();
}
有没有发现,老师自己封装了一个线程池的工具类?对!这里就是老师自己封装的工具模板,具体代码如下:
//采用单例模式的IoDH封装线程池的工具类
public class ThreadPoolUtil {
//线程池对象
public ThreadPoolExecutor poolExecutor;
//构造函数私有化
private ThreadPoolUtil(){
//完成线程池对象的实例化
poolExecutor=new ThreadPoolExecutor(4,10,3, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(20),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
//静态内部类 完成对象的实例化
private static class PoolInner{
private final static ThreadPoolUtil pool=new ThreadPoolUtil();
}
//获取唯一实例
public static ThreadPoolUtil getInstance(){
return PoolInner.pool;
}
}
代码中不仅使用了线程池,而且用到了设计模式,这里用的是单例设计模式,而且还是基于IoDH实现的。这样一来,不仅这个需求解决了,而且代码的逼格还有点高哦。
四. 小结
好了,老师想表达的就这么多,但最后还是要再给大家啰嗦几句。
线程池适合处理耗时的任务,可以充分使用目前服务器的硬件资源,加快处理的速度;
使用线程池时,不要死板,要结合自己的实际业务需求;
先写出基本的代码,然后再进行测试,一定要测试,至于说线程池的核心参数该如何设置,还是得根据你们实际的请求来决定。
相关推荐HOT
更多>>如何为Apple iOS设计动态岛?
Dynamic Island 是自 2022 年 9 月 iPhone 14 系列推出以来在 iPhone 屏幕最上方推出的新型药丸状切口。它围绕着 FaceID 传感器和前置摄像头,...详情>>
2023-02-21 17:55:40成为UI设计师需要具备哪些技能?
如果您对将 UI 设计作为一种潜在的职业道路感兴趣,您可能想知道:对于 UI 设计师而言,最重要的技能是什么?●了解基本设计原则,例如排版和色...详情>>
2023-02-21 17:51:57用户体验设计师做什么的?
当用户体验设计师完成用户研究分析并发现潜在问题或障碍后,他们将进入设计阶段。用户体验设计师创建流程图以鸟瞰产品的不同元素如何相互连接。...详情>>
2023-02-21 17:50:40设计的6大要素是什么?
伟大的设计不是偶然发生的。一位伟大的设计师会战略性地选择颜色、形状和版式等东西——所有这些都会影响观众或用户如何看待和参与一件作品。 详情>>
2023-02-21 17:46:00