线程池(Thread Pool)是一种管理和复用线程的机制,它在多线程编程中被广泛使用,主要目的是提高线程的利用率和性能,减少线程创建和销毁的开销。线程池通过预先创建一定数量的线程,并维护一个任务队列,有效地处理多个任务。
一、原理及工作流程
1、线程池初始化:
- 在创建线程池时,会指定池中的初始线程数量、最大线程数量、空闲线程的存活时间等参数。
- 线程池通常由一个线程池管理器(
ThreadPoolExecutor
或Executors
工厂类)负责创建和管理。
2、任务提交:
- 当有任务需要执行时,可以将任务提交给线程池。
- 任务可以是实现了
Runnable
接口或Callable
接口的对象。
3、任务队列:
- 线程池维护一个任务队列(通常是一个阻塞队列),用于存储提交但尚未执行的任务。
- 如果当前线程数未达到上限,并且有空闲线程,则任务会被分配给空闲线程执行;否则,任务会被放入任务队列等待执行
4、线程执行任务:
- 当有空闲线程可用时,线程池将任务从队列中取出,分配给空闲线程执行。
- 如果所有线程都在执行任务且任务队列已满(通常由队列类型决定是否会阻塞或抛出异常),线程池根据配置的策略(如丢弃、阻塞、调用者运行等)处理新任务的提交。
5、线程回收:
- 如果一个线程在一定时间内未被使用(空闲时间超过设定的存活时间),线程池可能会关闭并移除这个线程,以节省资源。
- 但如果线程池中的线程数低于初始数目,有些线程池实现会保持一定数量的核心线程,以避免频繁地创建和销毁线程。
二、ThreadPoolExecutor 参数说明
ThreadPoolExecutor
是一个强大的线程池实现类,它提供了多个参数来配置线程池的行为,这些参数可以根据实际需求进行调整,以优化线程的使用和任务的执行。以下是 ThreadPoolExecutor
的主要参数及其说明:
1、corePoolSize:
核心线程数。线程池中始终存活的线程数量,即使它们处于空闲状态也不会被销毁,除非设置了 allowCoreThreadTimeOut(true)
。这个值通常设置为能够快速处理任务的数量,即使线程空闲也能保持响应能力。
2、maximumPoolSize:
最大线程数。线程池中允许存在的最大线程数量。当工作队列已满且线程数小于最大线程数时,线程池会创建新的线程来处理任务,直到达到最大线程数为止。
3、 keepAliveTime:
线程空闲时间。当线程数大于核心线程数时,多余的空闲线程在终止之前等待新任务的最长时间。超过这个时间,多余的线程将被销毁,直到线程数量等于核心线程数为止。
4、unit:
线程空闲时间的单位。通常使用 TimeUnit
中的枚举值,例如 TimeUnit.MILLISECONDS
表示毫秒。
5、workQueue:
工作队列。用于保存等待执行的任务的阻塞队列。可以选择不同的队列实现,如 ArrayBlockingQueue
、LinkedBlockingQueue
、SynchronousQueue
等,每种队列实现都有其特点和适用场景 。
6、threadFactory:
线程工厂。用于创建新线程,默认情况下使用 Executors.defaultThreadFactory()
创建线程。通过自定义线程工厂,可以为线程池中的线程设置特定的名称、优先级等属性。
7、handler:
拒绝策略。当任务添加到线程池中被拒绝时的处理策略。常见的策略包括 AbortPolicy
(默认,抛出异常)、CallerRunsPolicy
(调用者执行任务)、DiscardPolicy
(默默丢弃任务)、DiscardOldestPolicy
(丢弃队列中最老的任务)。
三、ThreadPoolExecutor 拒绝策略
ThreadPoolExecutor
在任务提交到线程池时可能会遇到无法处理的情况,这时就会触发拒绝策略。拒绝策略定义了线程池无法继续接受新任务时的处理方式。Java 提供了四种预定义的拒绝策略,同时也支持自定义拒绝策略。
1、AbortPolicy(默认策略):
当任务添加到线程池被拒绝时,会抛出 RejectedExecutionException
异常。这是默认的拒绝策略,会立即抛出异常,防止任务堆积导致系统不可控。
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, maxPoolSize, keepAliveTime, unit,
workQueue, Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
2、CallerRunsPolicy:
当任务添加到线程池被拒绝时,会由添加任务的线程执行被拒绝的任务。这种策略不会丢弃任务,而是由调用者自己来执行该任务,从而降低新任务的提交速度。
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, maxPoolSize, keepAliveTime, unit,
workQueue, Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
3、DiscardPolicy:
当任务添加到线程池被拒绝时,会默默地丢弃被拒绝的任务。这种策略会造成任务的丢失,不建议在需要高可靠性的系统中使用。
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, maxPoolSize, keepAliveTime, unit,
workQueue, Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
4、DiscardOldestPolicy:
当任务添加到线程池被拒绝时,会丢弃队列中最老的一个任务,然后尝试重新提交被拒绝的任务。
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, maxPoolSize, keepAliveTime, unit,
workQueue, Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());
四、代码示例
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CustomThreadPoolExample {
public static void main(String[] args) {
// 定义线程池参数
int corePoolSize = 5; // 核心线程数
int maxPoolSize = 10; // 最大线程数
long keepAliveTime = 5000; // 线程空闲时间
ArrayBlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10); // 工作队列
// 创建 ThreadPoolExecutor
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.MILLISECONDS,
workQueue,
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());//DiscardOldestPolicy策略
// 提交任务给线程池执行
for (int i = 0; i < 15; i++) {
Runnable task = new Task(i);
executor.execute(task);
}
// 关闭线程池
executor.shutdown();
}
// 任务类
static class Task implements Runnable {
private int taskId;
public Task(int id) {
this.taskId = id;
}
@Override
public void run() {
System.out.println("任务ID : " + taskId + "被" + Thread.currentThread().getName() + "执行");
try {
Thread.sleep(1000); // 模拟任务执行时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}