线程池概念
1. 线程池的七大参数
线程池的核心参数通过 ThreadPoolExecutor
类定义:
-
corePoolSize
(核心线程数):- 线程池中始终保持存活的线程数量。
- 即使线程空闲,也不会被销毁。
-
maximumPoolSize
(最大线程数):- 线程池中允许的最大线程数量。
- 当任务队列满时,线程池会创建新线程,直到达到最大线程数。
-
keepAliveTime
(线程空闲时间):- 当线程数超过核心线程数时,空闲线程的存活时间。
- 超过该时间后,空闲线程会被销毁。
-
unit
(时间单位):keepAliveTime
的时间单位(如秒、毫秒)。
-
workQueue
(任务队列):- 用于存放待执行任务的阻塞队列。
- 常见队列类型:
LinkedBlockingQueue
、ArrayBlockingQueue
、SynchronousQueue
。
-
threadFactory
(线程工厂):- 用于创建新线程的工厂。
- 可以自定义线程的名称、优先级等。
-
handler
(拒绝策略):- 当任务队列满且线程数达到最大线程数时,如何处理新任务。
- 常见策略:
AbortPolicy
:抛出异常(默认)。CallerRunsPolicy
:由提交任务的线程执行。DiscardPolicy
:直接丢弃任务。DiscardOldestPolicy
:丢弃队列中最旧的任务
线程池概念、工作原理及 Executor 框架详解
一、线程池概念
线程池(Thread Pool) 是一种管理多线程的机制,通过预先创建并维护一组可复用的线程,避免频繁创建和销毁线程的开销,从而提高系统性能和资源利用率。其核心优势包括:
- 降低资源消耗:复用线程,减少线程创建/销毁的开销。
- 提高响应速度:任务到达时可直接使用空闲线程执行。
- 提高线程可控性:统一管理线程数量、任务队列和拒绝策略。
二、线程池工作原理
线程池的核心流程如下:
-
任务提交
用户通过execute()
或submit()
方法向线程池提交任务(Runnable
或Callable
对象)。 -
任务处理逻辑
- 核心线程未满:直接创建新线程执行任务。
- 核心线程已满,任务队列未满:将任务放入队列等待。
- 队列已满,线程数未达最大值:创建非核心线程执行任务。
- 队列和线程数均达上限:触发拒绝策略(如抛出异常、丢弃任务等)。
-
线程复用
线程执行完任务后不会立即销毁,而是保持存活并继续从队列中获取新任务。 -
线程回收
非核心线程在空闲超时后会被销毁,核心线程默认长期存活(可配置)。
以下是关于 LinkedBlockingQueue
、ArrayBlockingQueue
、SynchronousQueue
的差异说明,以及 execute()
和 submit()
方法的使用示例。
2. 阻塞队列的差异
队列类型 | 数据结构 | 容量 | 特性 | 适用场景 |
---|---|---|---|---|
LinkedBlockingQueue | 链表 | 可选有界/无界 | - 默认无界(Integer.MAX_VALUE )。- 吞吐量较高,适合任务处理较慢的场景。 | 需要缓冲大量任务的场景(如批处理系统)。 |
ArrayBlockingQueue | 数组 | 固定有界 | - 必须指定容量。 - 内存预分配,性能稳定。 - 队列满时触发拒绝策略或创建新线程。 | 需严格控制资源的高并发场景。 |
SynchronousQueue | 无存储 | 容量为 0 | - 不存储元素,直接传递任务。 - 插入操作需等待移除操作,反之亦然。 | 高吞吐量且任务处理极快的场景。 |
示例场景
-
LinkedBlockingQueue
适用于需要缓冲大量任务的场景(如异步日志处理)。ExecutorService executor = new ThreadPoolExecutor( 2, 4, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100) // 有界队列(容量100) );
-
ArrayBlockingQueue
适用于资源受限的高并发场景(如秒杀系统)。ExecutorService executor = new ThreadPoolExecutor( 2, 4, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10) // 固定容量10 );
-
SynchronousQueue
适用于要求即时响应的场景(如实时消息推送)。ExecutorService executor = new ThreadPoolExecutor( 2, 4, 60, TimeUnit.SECONDS, new SynchronousQueue<>() // 直接传递任务,无缓冲 );
3. execute()
与 submit()
的区别及示例
方法 | 参数类型 | 返回值 | 异常处理 | 适用场景 |
---|---|---|---|---|
execute() | Runnable | 无 | 异常需在任务内捕获或全局处理。 | 简单任务,无需获取结果。 |
submit() | Runnable /Callable | Future | 可通过 Future.get() 捕获异常。 | 需要获取结果或管理任务生命周期的场景。 |
1. execute()
示例
ExecutorService executor = Executors.newFixedThreadPool(2);
// 提交 Runnable 任务
executor.execute(() -> {
System.out.println("Task executed by: " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 关闭线程池
executor.shutdown();
2. submit()
示例
ExecutorService executor = Executors.newFixedThreadPool(2);
// 提交 Callable 任务并获取 Future
Future<Integer> future = executor.submit(() -> {
System.out.println("Callable task executed by: " + Thread.currentThread().getName());
return 42; // 返回计算结果
});
try {
// 获取任务结果(阻塞直到任务完成)
Integer result = future.get();
System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
// 关闭线程池
executor.shutdown();