为什么要使用线程池
ExecutorService利用池化线程执行任务,Executors的工厂方法可以创建线程池
线程池解决了两个问题:
- 减少每个线程创建消耗的时间,复用线程
- 提供管理线程,资源的边界
为了适应不同的业务需求,ExecutorService提供了很多可调节的参数和扩展机制,主要的参数解析如下:
-
核心线程数和最大线程数
ThreadPoolExecutor将会根据核心线程数和最大线程数自动调节线程数大小,当一个新任务被提交的时候如果线程池线程数小于核心线程数,即使这个时候其他线程是空闲的,新的线程也将被创建去处理请求,如果线程池线程数大于核心线程数小于最大线程数,那么只有当任务队列已满的情况下,新的线程才会被创建。如果创建的核心线程数和最大线程数相同,你可以创建一个具有固定线程数的线程池,如果设置最大线程数为一个非常大的数,例如Integer.MAX_VALUE,那么线程池讲允许无限创建线程去执行并发任务。通常情况下,核心线程数和最大线程数在构造线程池的时候就已经确定,但是可以通过setCorePoolSize和setMaximumPoolSize进行调节 -
按需构造
默认情况下,即使是核心线程也是在任务到达的时候才会被初始化,但是如果你使用了 prestartCoreThread 或者prestartAllCoreThreads,那么可以构造一个非空的线程池 -
创建新的线程
线程通过ThreadFactory被创建,如果构造的时候不指定ThreadFactory,那么默认为Executors.defaultThreadFactory。defaultThreadFactory创建的线程的线程组相同,线程优先级为NORM_PRIORITY,并且为非守护线程状态。通过传递一个不同的ThreadFactory,你可以改变线程的名称,线程组,线程优先级,守护状态等等,如果ThreadFactory创建线程失败,executor能够继续存活但不能继续处理任务,线程应该具备modifyThread的运行时权限,如果使用池的工作线程或其他线程不具有此权限,则服务可能会降级:配置更改可能无法及时生效,并且关闭池可能仍处于可以终止但未完成的状态。 -
线程存活时间
如果线程数超过核心线程数,那么超过核心线程数的线程将在空闲时间超过存活时间后消亡,这种机制保证了在线程池不活跃的收资源能够被回收,当线程池变得活跃后,新的线程又会被创建,如果存活时间设置为Integer.MAX_VALUE,那么线程将不会被回收, allowCoreThreadTimeOut(boolean)方法可以控制核心线程数是否被回收,前提是存活时间不为零 -
阻塞队列
任何的阻塞队列都是被用于传递和保存任务,阻塞队列的使用和线程池的大小相关
- 少于核心线程数的时候,Executor将不会把任务放进队列,而是直接创建新线程
- 如果线程数超过核心线程数,那么Executor将优先将任务放进队列,而不是创建新线程
- 如果一个请求无法被放进阻塞队列(已满),那么新的线程将会被创建,如果超过了核心线程数,那么任务将会被拒绝
队列的三个通用策略
- 直接传递
- 无界队列
- 有界队列