作者:Java马剑威
链接:https://www.zhihu.com/question/299968861/answer/2650651340
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
最近有不少小伙伴私聊我,问我能不能讲讲项目中怎么使用线程池?所以,今天我就给大家安排一下,满足粉丝们的要求,给大家讲一下之前我在项目中使用线程池的一些经验。
我们想要在项目中使用线程池,就必须要知道线程池是啥、咋用?所以请各位先跟着我学学关于线程池的基础知识吧。
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 线程池的工作过程
那么上述代码中的线程池,是怎么处理线程任务的呢?且来听我给你唠唠,线程池接收到一个线程任务后,会经过如下步骤:
以上就是关于线程池的一些基本常识。
言归正传,咱们再来说一下在项目中到底该如何使用线程池。我之前给某个公司做过一个项目,其中有个业务,要满足大批量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<Department>((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实现的。这样一来,不仅这个需求解决了,而且代码的逼格还有点高哦。
好了,我想表达的就这么多,但最后还是要再给大家啰嗦几句。