线程池,及7大参数,4大拒绝策略详解
线程池是什么
池化技术我们经常见,比如学习spring JDBC要学习数据库连接池,学习Http网络协议的时候要引入http连接池.同样的,多线程也有与之对应的线程池.池化技术的思想主要是为了减少每次获取,对系统资源的消耗提高资源的利用率.
-
我们知道在面向对象编程中,创建和销毁对象是很费时间的,因为线程其实也是一个对象,创建一个对象,需要经过类加载过程,销毁一个对象,需要走GC垃圾回收流程,都是需要资源开销的。
-
重复利用.线程用完,再放回池子,可以达到重复利用的效果,节省资源。
-
提高响应速度. 如果任务到达了,相对于从线程池拿线程,重新去创建一条线程执行,速度肯定慢很多。
-
提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗
系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调
优和监控。
- 附加功能:提供定时执行、定期执行、单线程、并发数控制等功能。
线程池的工作流程
- 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
- 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
-
如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
-
如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
-
如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还
是要创建非核心线程立刻运行这个任务;
- 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线
程池会根据拒绝策略来对应处理。
-
当一个线程完成任务时,它会从队列中取下一个任务来执行。
-
当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,
如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池
的所有任务完成后,它最终会收缩到 corePoolSize 的大小。
线程池七大参数
线程池有七大参数,需要重点关注 corePoolSize 、 maximumPoolSize 、
workQueue 、 handler 这四个。
-
corePoolSize
表示线程池的核心线程数,即在池中一直保持的线程数。当有新任务提交时,如果当前池中的线程数小于corePoolSize
,则会创建一个新的线程来处理该任务。如果线程数已经达到corePoolSize
,则新的任务会被放入到线程池的工作队列中。 -
maximumPoolSize
表示线程池允许的最大线程数,包括核心线程数和非核心线程数。当线程池中的工作队列(通常是一个阻塞队列,比如BlockingQueue
)已满,并且线程数小于maximumPoolSize
时,线程池会创建新的非核心线程来处理任务。- 这样的设计允许线程池在处理大量任务时动态地增加线程数量,以提高并发性能。当任务数很大时,线程池可以通过创建额外的线程来处理任务,而当任务数减少时,线程池可以通过关闭多余的线程来释放资源
-
keepAliveTime
是用于设置非核心线程的闲置时间的参数。对于线程池中的非核心线程(即线程池的最大线程数超过核心线程数的部分),如果它在闲置状态超过了keepAliveTime
设置的时间,那么这个线程将被终止并从线程池中移除。 -
unit:线程池中非核心线程保持存活的时间的单位
TimeUnit.DAYS; 天
TimeUnit.HOURS; 小时
TimeUnit.MINUTES; 分钟
TimeUnit.SECONDS; 秒
TimeUnit.MILLISECONDS; 毫秒
TimeUnit.MICROSECONDS; 微秒
TimeUnit.NANOSECONDS; 纳秒
-
workQueue(等待队列):线程池等待队列,维护着等待执行的 Runnable 对象。当运行当线程数=
corePoolSize时,新的任务会被添加到 workQueue 中,如果 workQueue 也满了则
尝试用非核心线程执行任务,等待队列应该尽量用有界的。
-
threadFactory: 创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。
-
handler: corePoolSize 、 workQueue 、 maximumPoolSize 都不可用的时候执行的饱和策
略
线程池的拒绝策略
-
AbortPolicy(默认策略):当任务无法被提交给线程池时,会抛出RejectedExecutionException异常。
-
CallerRunsPolicy:当任务无法被提交给线程池时,会由提交任务的线程来执行该任务。这种策略可以降低任务的提交速度,但可以保证任务的执行。
-
DiscardOldestPolicy:
DiscardOldestPolicy
会丢弃队列中等待时间最长的任务,然后尝试再次提交被拒绝的任务。 -
DiscardPolicy:当任务无法被提交给线程池时,会默默地丢弃该任务,不会抛出异常也不会执行任务。这种策略会导致任务的丢失,不推荐在生产环境中使用。
-
**自定义拒绝策略:**实现
RejectedExecutionHandler
接口,自定义拒绝策略
public class MyCustomPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 自定义处理逻辑
System.out.println("自定义拒绝策略:" + r.toString());
}
}
然后将这个自定义策略设置给线程池:
executor.setRejectedExecutionHandler(new MyCustomPolicy());