java 线程池原理浅析

一次查询大数据慢?试试多线程分页查询呢?优雅又高效,下面我们了解一下多线程的池化的原理。

以下为本文目录:

一、问题与解决

二、线程池是什么?

三、Java 线程池参数解析

四、线程池工作原理

五、java 提供的常用线程池

问题与解决:

问题:

查询大数据量的时候,例如一次返回 50w 数据量的包,循环去查询发现读取会超时。

解决方案:

经过思考采用多线程去分页查询。使用线程池创建多个线程去查询分页后的数据最后汇总一下,解决了一次查询大量数据返回超时的问题。

一次查询现状:

多线程分页查询改造图:

效果:使用多线程去查询查询时间由原来的 10s 减少到现在的 300ms。

线程池是什么?

线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如 MySQL,clickhouse 等数据库。

线程池好处:

1.降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。

2.提高响应速度:任务到达时,无需等待线程创建即可立即执行。

3.提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。

4.使用线程池可以进行统一的分配、调优和监控。提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池 ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

线程池解决的问题是什么:

1.频繁申请/销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。

2.对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。

3.系统无法合理管理内部的资源分布,会降低系统的稳定性。

Java 线程池参数解析:

Java 提供的线程池相关的工具类中,最核心的是 ThreadPoolExecutor,完备的构造函数如下:

arduino 复制代码
ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) 

参数说明:

corePoolSize:线程池的核心线程数

备注:默认情况下,线程池创建后的初始线程数为 0,当有任务到来就会创建一个线程去执行任务,当线程池中的线程数目达到 corePoolSize,就会把到达的任务放到缓存队列当中。

maximumPoolSize:线程池的最大线程数

备注:核心线程忙不过来且任务存储队列满了的情况下,还有新任务进来的话就会继续开辟线程,但是也不是任意的开辟线程数量,线程数(包含核心线程)达到 maximumPoolSize 后就不会产生新线程了,就会执行拒绝策略。

keepAliveTime:当活跃线程数大于核心线程数时,空闲的多余线程最大存活时间

备注:默认情况下,当线程池中的线程数大于 corePoolSize 时,如果一个线程空闲的时间达到 keepAliveTime,则该线程会终止,直到线程池中的线程数不超过 corePoolSize。但是如果调用了 allowCoreThreadTimeOut(true) 方法,此时就算线程池中的线程数不大于 corePoolSize ,keepAliveTime 参数也会起作用,直到线程池中的线程数为 0。

unit:参数 keepAliveTime 的时间单位。

取值:

perl 复制代码
TimeUnit.DAYS;               //天TimeUnit.HOURS;             //小时 TimeUnit.MINUTES;           //分钟 TimeUnit.SECONDS;           //秒TimeUnit.MILLISECONDS;      //毫秒 TimeUnit.MICROSECONDS;      //微妙TimeUnit.NANOSECONDS;       //纳秒 

workQueue:任务阻塞队列,用来存储等待执行的任务。

备注:

核心线程数满了后还有任务继续提交到线程池的话,就先进入 workQueue。

workQueue 通常情况下有如下选择:

LinkedBlockingQueue:无界队列,意味着无限制,其实是有限制,大小是 int 的最大值。也可以自定义大小。

ArrayBlockingQueue:有界队列,可以自定义大小,到了阈值就开启新线程(不会超过 maximumPoolSize)。

SynchronousQueue:Executors.newCachedThreadPool();默认使用的队列。也不算是个队列,他不没有存储元素的能力。

一般都采取 LinkedBlockingQueue,因为他也可以设置大小,可以取代 ArrayBlockingQueue 有界队列。

threadFactory:用于创建新线程的线程工厂。

备注:默认采用的是 DefaultThreadFactory,主要负责创建线程。newThread()方法。创建出来的线程都在同一个线程组且优先级也是一样的。

handler:拒绝策略,任务量超出线程池的配置限制或执行 shutdown 还在继续提交任务的话,会执行 handler 的逻辑。

备注:

如果线程池中所有的线程都在忙碌,并且工作队列也满了(前提是工作队列是有界队列),那么此时提交任务,线程池就会拒绝接收。拒绝策略可以通过 handler 参数来指定。

ThreadPoolExecutor 提供了以下 4 种拒绝策略:

CallerRunsPolicy:提交任务的线程自己去执行该任务。

AbortPolicy:默认的拒绝策略,会 throws RejectedExecutionException。

DiscardPolicy:直接丢弃任务,没有任何异常抛出。

DiscardOldestPolicy:丢弃最老的任务,就是把最早进入工作队列的任务丢弃,然后把新任务加入到工作队列。

线程池工作原理:

java 提供的常用线程池:

1.newCachedThreadPool:用来创建一个可以无限扩大的线程池,适用于负载较轻的场景,执行短期异步任务。(可以使任务快速得到执行,因为任务时间执行短,可以很快结束,也不会造成 cpu 过度切换)

2.newFixedThreadPool:创建一个固定大小的线程池,因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于负载较重的场景,对当前线程数量进行限制。(保证线程数可控,不会造成线程过多,导致系统负载更为严重)

3.newSingleThreadExecutor:创建一个单线程的线程池,适用于需要保证顺序执行各个任务。

4.newScheduledThreadPool:适用于执行延时或者周期性任务。

作者:智能平台 郭春元

来源:京东零售技术 转载请注明来源

相关推荐
陈平安Java and C5 小时前
MyBatisPlus
java
秋野酱5 小时前
如何在 Spring Boot 中实现自定义属性
java·数据库·spring boot
安的列斯凯奇5 小时前
SpringBoot篇 单元测试 理论篇
spring boot·后端·单元测试
Bunny02126 小时前
SpringMVC笔记
java·redis·笔记
架构文摘JGWZ6 小时前
FastJson很快,有什么用?
后端·学习
BinaryBardC6 小时前
Swift语言的网络编程
开发语言·后端·golang
feng_blog66886 小时前
【docker-1】快速入门docker
java·docker·eureka
邓熙榆6 小时前
Haskell语言的正则表达式
开发语言·后端·golang
枫叶落雨2228 小时前
04JavaWeb——Maven-SpringBootWeb入门
java·maven
m0_748232398 小时前
SpringMVC新版本踩坑[已解决]
java