文末有福利领取,请耐心阅读哦~
什么是线程池?
线程池是为了提高程序执行效率,尽量减少线程对象的创建和销毁的次数而产生的一种技术。线程池内部维护了两个集合,一个是线程的集合,另一个是任务集合。
线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
阿里Java开发手册中强烈要求我们不允许使用Executors来创建线程池对象,而要通过ThreadPoolExecutor的方式,这是为什么呢,下面就说一下我个人看法。
线程池参数说明
在开始之前我先给大家讲一下线程池的基本知识,ThreadPoolExecutor有多个构造参数,但最终都会调用到下面的这个构造中来,所以我直接讲参数最全的这个构造方法。
参数 | 说明 |
---|---|
corePoolSize | 核心线程 |
maximumPoolSize | 最大线程数 |
keepAliveTime | 存活时间 |
timeUnit | 时间单位 |
workQueue | 任务队列 |
threadFactory | 线程工厂 |
rejectedExecutionHandler | 拒绝策略 |
-
corePoolSize的作用是代表要开启核心线程数量,核心线程会一直保留。
-
maximumPoolSize的作用是最大可以创建的线程数量,当你的所有核心线程都在工作状态时,此时如果有新的任务需要执行,系统就会创建新的线程来执行任务。
-
keepAliveTime代表新开启的线程如果执行完毕后可以存活多长时间,如果在设置的时间内没有任务使用该线程,则线程资源就会归还操作系统。
-
timeUnit 代表线程存活的时间单位。
-
workQueue 任务队列,如果正在执行的任务超过了最大线程数,可以存放在队列中,当线程池中有空闲资源就可以从队列中取出任务继续执行。
队列类型有如下几种LinkedBlockingQueue ArrayBlockingQueue SynchronousQueue TransferQueue,使用不同的队列就会产生不同类型的线程池。(任务队列的知识,我会单独的去写一篇文章)
-
threadFactory 线程工厂,他的作用是用来产生线程的,可以自定义线程的类型,比如我们可以定义线程组名称,在jstack问题排查时,非常有帮助。
-
rejectedExecutionHandler 拒绝策略, 当所有线程都在忙,并且任务队列处于满任务的状态,则会执行拒绝策略。
拒绝策略可以自定义,JDK默认给我们提供了4种,分别是:
1.Abort: 直接抛出异常。
2.Discard: 直接扔掉最新的任务,不抛异常。
3.DiscardOldest: 扔掉排队时间最久的任务,也就是最旧的任务。
4.CallerRuns: 调用者处理任务。
Executors开启线程池的方式
Executors是一个工具类,直接使用Executors可以调用JDK为我们提供的线程池方法。
Executors.newCachedThreadPool(); // 创建可缓存的线程池
Executors.newFixedThreadPool(1); // 创建固定大小的线程池
Executors.newSingleThreadExecutor(); // 创建单线程池
- newCachedThreadPool是一个可缓存的线程池,当线程池的线程数量大于需要执行的任务数量时,则会在60秒后回收掉线程资源。
当任务量增加,线程池也会创建出对应的线程来执行任务。
- newFixedThreadPool是一个固定大小的线程池,传入的参数是用来设置最大线程数的,如果此时线程都在执行状态,则会向任务队列中放入任务。
- newSingleThreadExecutor是一条单线程的线程池,此线程池可以保证线程按照提交顺序来完成。
通过上述源码分析,我们发现newFixedThreadPool和newSingleThreadExecutor方法他们都使用了LinkedBlockingQueue的任务队列,LikedBlockingQueue的默认大小为Integer.MAX_VALUE。而newCachedThreadPool中定义的线程池大小为Integer.MAX_VALUE。
所以阿里禁止使用Executors创建线程池的原因就是FixedThreadPool和SingleThreadPool的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
CachedThreadPool允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
总结
阿里的开发手册中的这些要求是基于他们的访问量来定义的,并不是说我们所有公司都禁止使用Executors来创建线程池,毕竟我们大多数公司的业务访问量与阿里根本没得比。
所以我们在日常开发时候还是可以用的,但这篇文章目的是让大家站在顶尖互联网公司角度去思考问题,毕竟每个热爱编程的程序员最终梦想不都是进入BAT嘛,加油!!!