如何使用好线程池?
如何使用好线程池?
- 线程个数大小的设置
- 线程池相关参数的配置
- 利用Hook嵌入你的行为
- 线程池的关闭
线程池数量的设置,你的依据是什么?
计算机密集型
应用需要非常多的CPU计算资源,避免过多的线程上下文切换
线程数 = CPU核数+1,已可以设置为CPU核数*2,还要看JDK的版本以及CPU配置(服务器的CPU有超线程)
IO密集型
Web应用,涉及到大量的网络传输,不仅如此,与数据库,与缓存间的交互也涉及到IO,一旦发生IO,线程就会处于等待状态,当IO结束后,数据准备好后,线程才会继续执行。对于IO密集型的应用,我们可以多设置线程池中线程的数量,这样就能让等待IO的这段时间内,线程可以去做其它事,提高并发处理效率。线程上下文切换数有代价的
线程数 = CPU核数/(1-阻塞系数) 这个阻塞系数一般为0.8~0.9之间。
套用公司:对于双核CPU来说,比较理想的线程数就是20,当然这不是绝对的,需要根据实际情况以及实际业务来调整:final int poolSIze = (int)(copCore/(1-0.9))
线程池相关参数如何配置?
- 高压线:
- 我们使用线程池的时候都不要选择没有上限限制的配置项
- 第一 我们不要去使用没有上限的线程池和设置无界队列!
- 比如,newCachedThreadPool的设置与无界队列因为某些不可预期的情况,线程池会出现系统异常,导致线程暴增的情况或者任务队列或者任务队列不断膨胀,内存耗尽导致系统崩溃。我们建议自定义线程池来避免改问题,这是在使用线程池的首要选择。小心无大错,千万别过度自信。
- 第二 合理设置线程数量、和线程空闲回收时间,根据具体的任务执行周期和时间去设定,避免频繁的回收和创建,虽然我们使用线程池的目的是为了提升系统性能和吞吐量,但是也要考虑下系统的稳定性,不然出现不可预期问题会很麻!
- 第三,根据实际场景,选择适用于自己的拒绝策略。进行补偿,不要乱用JDK支持的自动补偿机制~尽量采用自定义的拒绝策略去进行兜底!
利用Hook去嵌入你的行为
- 利用Hook,留下线程池执行的执行轨迹
- ThreadPoolExcutor提供了protected类型可以被覆盖的钩子方法,运行用户在执行任务之前或执行之后做一些事情。我们可以通过它来实现比如初始化ThreadLocal、收集统计信息、如记录日志等操作,这类Hook如BeforeExcute和afterExecute。另外还有一个Hook可以用来在任务呗执行完的时候让用户插入逻辑,如reminated。
- Hook方法执行失败,则内部的工作线程的执行将会失败或者中断。
关闭线程池
- 内容当线程池不在被引用并且工作线程数为0的时候,线程池将被终止。我们也可以调用shutdown来手动终止线程池。如果我们忘记调用shutdown,为了让线程资源被释放,我们还可以使用keepAliveTime和allowShutdownHook方法,手工去调用线程池的关闭方法!
- 当然,最稳妥的方式是使用虚拟机Runtime.getRuntime().addShutdownHook方法,手动去调用线程池关闭方法。