Java线程池参数和方法
1. 线程池状态
ThreadPoolExecutor 使用 int 的高 3 位来表示线程池状态,低 29 位表示线程数量
状态名 | 高 3 位 | 接收新任务 | 处理阻塞队列任务 | 说明 |
---|---|---|---|---|
RUNNING | 111 | Y | Y | |
SHUTDOWN | 000 | N | Y | 不会接收新任务,但会处理阻塞队列剩余 任务 |
STOP | 001 | N | N | 会中断正在执行的任务,并抛弃阻塞队列 任务 |
TIDYING | 010 | 任务全执行完毕,活动线程为 0 即将进入 终结 | ||
TERMINATED | 011 | 终结状态 |
2. 线程池参数
ThreadPoolExecutor executor = new ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
java线程池有四个构造方法,上面的为其中一个,参数最全的,另外三个构造方法在这个构造方法中少一到两个参数。
2.1 int corePoolSize
代表线程池中核心线程数目,也即是最多保留线程数目(当线程处于空闲状态时,核心线程不会被回收,依然存在)
2.2 int maximumPoolSize
代表最大线程数目,即当核心线程数都被占用时,工作队列中依然还有任务时,会临时创建新的线程。当解决任务后,该线程就会被摧毁。(相当于临时工)
2.3 long keepAliveTime
代表生存时间,当线程空闲时,经过指定时间后被摧毁。针对的时救急线程(临时工)
2.4 TimeUnit unit
代表keepAliveTime计量单位
2.5 BlockingQueue workQueue
代表阻塞队列即工作队列。新任务会添加到任务队列当中,等待有线程空闲获取任务。
在jdk中一般有四种队列:
2.5.1 ArrayBlockingQueue
基于数组的有界阻塞队列,指定阻塞队列大小。每当有任务时,添加致队列末尾,而线程每次从头获取任务,当任务队列满时,再添加任务就会采取拒绝策略。有界的数组可以防止资源耗尽问题。
2.5.2 LinkedBlockingQuene
基于链表的*阻塞队列,实际上上限是(Integer.MAX_VALUE)。即主要有任务,就会一直压入任务队列当中。但是只会启用核心线程来执行任务,并不会启动应急线程。因此,最好将corePoolSize和maximumPoolSize设为一致。
2.5.3 SynchronousQuene
是一个不缓存任务的阻塞队列,当存入任务来临时,必须等待线程调用。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maximumPoolSize,则执行拒绝策略。
2.5.4 PriorityBlockingQueue
具有优先级的*阻塞队列,优先级通过参数Comparator实现。
2.6 ThreadFactory threadFactory
创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
2.7 RejectedExecutionHandler handler
**拒绝策略。当任务队列中达到任务上限时,如果再继续添加就会采用拒绝策略,通过拒绝策略来处理新的任务。**不同的拒绝策略有着不同的方法,jdk中默认有四种拒绝策略:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
3 Executors常用工厂方法
3.1 newFixedThreadPool
评价:适用于任务量已知,相对耗时的任务
// 核心线程数 == 最大线程数(没有救急线程被创建),因此也无需超时时间
// 阻塞队列是*的,可以放任意数量的任务
Executors.newFixedThreadPool(5);
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
3.2 newCachedThreadPool
评价:整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲 1分钟后释放线 程。 适合任务数比较密集,但每个任务执行时间较短的情况
// 核心线程数是 0, 最大线程数是 Integer.MAX_VALUE,救急线程的空闲生存时间是 60s,意味着
// 全部都是救急线程(60s 后可以回收)
// 救急线程可以无限创建
// 队列采用了 SynchronousQueue 实现特点是,它没有容量,没有线程来取是放不进去的(一手交钱、一手交货)
Executors.newCachedThreadPool();
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3.3 newSingleThreadExecutor
// 希望多个任务排队执行。线程数固定为 1,任务数多于 1 时,会放入*队列排队。任务执行完毕,这唯一的线程也不会被释放。
Executors.newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
这里需要说明下使用该方法创建的线程池(只有一个线程)和自己创建一个单线程有什么区别:
- 自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池还会新建一 个线程,保证池的正常工作
使用该方法创建的线程池(只有一个线程)和使用Executors.newFixedThreadPool(1)创建线程池区别:
-
Executors.newSingleThreadExecutor() 线程个数始终为1,不能修改
- FinalizableDelegatedExecutorService 应用的是装饰器模式,只对外暴露了 ExecutorService 接口,因此不能调用 ThreadPoolExecutor 中特有的方法
-
Executors.newFixedThreadPool(1) 初始时为1,以后还可以修改
- 对外暴露的是 ThreadPoolExecutor 对象,可以强转后调用 setCorePoolSize 等方法进行修改
4. 提交任务
// 执行任务
void execute(Runnable command);
// 提交任务 task,用返回值 Future 获得任务执行结果
<T> Future<T> submit(Callable<T> task);
// 提交 tasks 中所有任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
// 提交 tasks 中所有任务,带超时时间
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit) throws InterruptedException;
// 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消
<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
// 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消,带超时时间
<T> T invokeAny(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
5. 关闭线程池
5.1 shutdown
/*
线程池状态变为 SHUTDOWN
- 不会接收新任务
- 但已提交任务会执行完
- 此方法不会阻塞调用线程的执行
*/
void shutdown();
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 修改线程池状态
advanceRunState(SHUTDOWN);
// 仅会打断空闲线程
interruptIdleWorkers();
onShutdown(); // 扩展点 ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 尝试终结(没有运行的线程可以立刻终结,如果还有运行的线程也不会等)
tryTerminate();
}
5.2 shutdownNow
/*
线程池状态变为 STOP
- 不会接收新任务
- 会将队列中的任务返回
- 并用 interrupt 的方式中断正在执行的任务
*/
List<Runnable> shutdownNow();
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 修改线程池状态
advanceRunState(STOP);
// 打断所有线程
interruptWorkers();
// 获取队列中剩余任务
tasks = drainQueue();
} finally {
mainLock.unlock();
}
// 尝试终结
tryTerminate();
return tasks;
}