ThreadPoolExecutor线程池原理

1. ThreadPoolExecutor是什么

有一定开发经验的java程序员不会直接使用Thread或Runnable来创建异步任务,从而让这个任务在另一个线程中运行,而一般都是使用线程池,即ThreadPoolExecutor。线程池就是一个在内部维护很多Worker线程和一个阻塞队列的服务对象(ExecutorService),其中的线程不断从阻塞队列取任务并执行,如下图所示。

Java并发工具ThreadPoolExecutor线程池使用讲解1

2. 线程池如何提高系统的性能

线程池中的Worker线程不断从队列取出任务并且执行,同一个Worker线程完成任务之后,如果时间片尚未用完,还可以继续到队列中取任务,所以线程上下文切换次数被降低,如下图所示,不必要的下文切换次数被减少。

Java并发工具ThreadPoolExecutor线程池使用讲解1

注意:如果你的任务执行的是IO操作或网络请求,有阻塞动作,那么把这类任务提交到线程池不能减少上下文切换次数,因而不能提高性能(这种情况要想提高性能需使用Java的NIO模型,今后会讲解)。

3. 线程池中的线程数、阻塞队列大小可以设置限制,从而避免系统负载超出范围,合理使用系统资源。这些限制主要是通过创建线程池的5个参数来控制的。

JDK为了让开发方便,提供了Executors工厂方法,预配置好这5个参数。

4. 线程池的corePoolSize和maximumPoolSize参数

corePoolSize和maximumPoolSize控制线程池中的worker线程个数。线程池刚创建出来的时候,线程池中的线程个数poolSize=0;当有任务提交进来,不管线程是否空闲,poolSize均增长,直到poolSize等于corePoolSize。

当poolSize >= corePoolSize之后,任务开始储存在队列中,当队列满了之后,线程池才会再创建线程。假设每个提交给线程池的任务的运行时间无限长,则线程数、corePoolSize、mamximuPoolSize、阻塞队列之间的关系如图所示。

Java并发工具ThreadPoolExecutor线程池使用讲解1

可以通过调用prestartCoreThread方法或prestartAllCoreThreads提前让线程池提前创建好corePoolSize所指定数量的线程,而不是被动等待任务到来再创建core thread。

5. 线程池的ThreadFactory参数

线程池中的线程都是由一个ThreadFactory创建的,你可以自己指定一个,也可以使用默认的。如果需要修改worker线程的线程组、优先级、线程名称、daemon状态,我们才需要指定ThreadFactory。

6. 线程池的Keep Alive机制

当worker线程数量超过corePoolSize时,每个空闲的worker会记录自己的空闲时间,当worker线程的空闲时间超过keepAliveTime参数时,worker线程将终止。如果把keepAliveTime=Long.MAX_VALUE,则空闲时间不可能达到这个值,线程创建之后就一直存在,直到整个线程池被关闭。

7. 线程池中的阻塞队列

阻塞队列与corePoolSize和maximumPoolSize之间有很密切的关系,已经在前面中介绍过了。再总结一下,当线程数小于corePoolSize,则线程池优先创建线程来执行任务;当线程数大于corePoolSize、小于maximumPoolSize,则线程池优先将任务放入队列;当线程数大于corePoolSize且队列满了之后,再创建线程来执行任务;当线程数大于maximumPoolSize且队列也满,则按照饱和策略执行相应动作。

发布了259 篇原创文章 · 获赞 316 · 访问量 93万+

猜你喜欢

转载自blog.csdn.net/u014756827/article/details/88814466