介绍
Java线程池(Executor Framework)是Java并发包java.util.concurrent提供的一个框架,用于管理一组工作线程(线程池),通过重用现有的线程而不是为每个任务创建新线程,来减少线程创建和销毁的开销,从而提高程序性能。
线程池的状态
Java线程池中的ThreadPoolExecutor类定义了线程池的几种状态:
- RUNNING:接受新任务,处理排队的任务。
- SHUTDOWN:不接受新任务,但处理已排队的任务。
- STOP:不接受新任务,不处理已排队的任务,并中断正在处理的任务。
- TIDYING:所有任务都终止了,workerCount为0,线程池状态将变为TIDYING,然后执行terminated()方法。
- TERMINATED:terminated()方法完成执行后的状态。
创建线程池的几种方式
- Executors.newFixedThreadPool(int nThreads):创建一个固定大小的线程池。
- Executors.newCachedThreadPool():创建一个可缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
- Executors.newSingleThreadExecutor():创建一个单线程的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
- Executors.newScheduledThreadPool(int corePoolSize):创建一个定长线程池,支持定时及周期性任务执行。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler):通过ThreadPoolExecutor类直接创建,参数灵活,可自定义线程池的大小、任务队列、拒绝策略等。
线程池各参数的说明
- corePoolSize:核心线程数,即使它们空闲,线程池也会保持线程的数量。
- maximumPoolSize:最大线程数,线程池允许的最大线程数。
- keepAliveTime:当线程数大于核心时,这是多余空闲线程在终止前等待新任务的最长时间。
- unit:keepAliveTime参数的时间单位。
- workQueue:用于存放任务的阻塞队列。
- threadFactory:用于创建新线程的工厂。
- handler:由于超出线程范围和队列容量而使任务被拒绝的处理器。
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
// 示例:使用ThreadPoolExecutor创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
10, // 非核心线程闲置超时时间
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10), // 阻塞队列
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
// 提交任务
for (int i = 0; i < 20; i++) {
int taskId =i;
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " is processing " + taskId);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 关闭线程池
executor.shutdown(); // 优雅关闭
// executor.awaitTermination(60, TimeUnit.SECONDS); // 等待线程池中的任务完成
// 或者立即尝试停止
// executor.shutdownNow();
// 注意:实际使用中,应该根据业务场景选择是否等待线程池中的任务完成
}
在这个示例中,我们创建了一个ThreadPoolExecutor
实例,并配置了核心线程数、最大线程数、非核心线程的超时时间、任务队列以及拒绝策略。然后,我们提交了20个任务到线程池。在实际应用中,你可能需要调用shutdown()
或shutdownNow()
来关闭线程池,具体取决于你的需求。如果你希望线程池中的任务都完成后再关闭,应该调用shutdown()
并可能使用awaitTermination()
来等待。如果你需要立即停止所有任务,可以调用shutdownNow()
。
注意,在调用shutdown()
或shutdownNow()
之后,应该避免再向线程池提交任务,因为这些任务可能会被拒绝。同时,如果线程池中的任务执行了中断敏感的操作(如调用了Thread.sleep()
),那么在调用shutdownNow()
时,这些任务应该能够响应中断,并尽早退出。
阻塞队列Handle有哪几种?
- ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列(但大小默认为Integer.MAX_VALUE)。
- SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,反之亦然。
- PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
拒绝策略有哪几种?
- ThreadPoolExecutor.AbortPolicy:默认策略,直接抛出RejectedExecutionException。
- ThreadPoolExecutor.CallerRunsPolicy:在调用者线程中直接运行被拒绝的任务。
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后尝试重新提交被拒绝的任务。
- ThreadPoolExecutor.DiscardPolicy:不处理被拒绝的任务,直接丢弃。
停止线程池的方式与区别
- shutdown():启动线程池的关闭序列,不再接受新任务,但会继续处理队列中的任务。
- shutdownNow():尝试停止所有正在执行的活动任务,停止处理正在等待的任务,并返回等待执行的任务列表。
区别:shutdown()是平滑地关闭线程池,等待正在执行的任务完成;而shutdownNow()是尝试立即停止所有任务,包括正在执行的任务,并尝试返回那些还未开始执行的任务。