为什么要使用线程池?
因为在Java开发中线程的创建开销很大,需要JVM和OS完成大量工作。在高并发应用中频繁创建和销毁线程的操作是很低效的行为。
线程池的主要工作是:
- 提升性能:线程池能独立负责线程的创建、分配和维护。当执行大量异步任务时,将创建线程、调度线程和销毁线程的工作交给线程池,能够让其尽可能使用空闲的线程去执行异步任务,尽可能实现线程的复用,使得效率明显提升。
- 线程管理:线程池都会存储一些基本的线程统计信息,这些信息可以帮助我们对线程进行有效的监控和管理。
Java开发中创建线程的方法
通过Executors创建线程池
这是Java内置的几种线程池的创建方法。
不推荐在开发中使用,由于Executors的线程池创建使用的默认参数往往不符合开发的需求,尤其是默认使用无界队列(如LinkedBlockingQueue),会导致OOM问题(内存溢出)。
手动创建线程的方式
使用ThreadPoolExecutor,我们可以手动创建合适我们使用的线程池。
ThreadPoolExecutor构造函数的7个参数
- corePoolSize:线程池中核心线程数
- maximumPoolSize:线程池中能拥有最大线程数
- keepAliveTime:表示空闲线程的存活时间
- unit:表示keepAliveTime的单位
- workQueue:用于缓存任务的阻塞队列
- threadFactory:创建新线程时使用的工厂
- handler:拒绝策略
public class ThreadPoolExample {
public static void main(String[] args) {
// 定义线程池参数
int corePoolSize = 5; // 核心线程数
int maximumPoolSize = 10; // 最大线程数
long keepAliveTime = 60; // 线程空闲时间
TimeUnit unit = TimeUnit.SECONDS; // 空闲时间单位
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10); // 任务队列
ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 线程工厂
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // 拒绝策略
// 创建线程池
ExecutorService executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);
// 提交任务到线程池
for (int i = 1; i <= 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("线程: " + Thread.currentThread().getName());
// 这里可以放具体的任务逻辑
// ...
});
}
// 关闭线程池
executor.shutdown();
}
}
四种拒绝策略的介绍
-
AbortPolicy
默认的拒绝策略是中止策略。当线程池无法接受新的任务时,中止策略会让executor抛出一个RejectedExecutionException。
适用场景:适用于对任务丢失敏感的场景,当线程池无法接受新任务时,希望立即知道并处理该异常。
-
CallerRunsPolicy
CallerRunsPolicy会让调用者线程来执行任务,也就是采取同步调用的方式,间接控制了任务提交的流量。
适用场景:适用于希望控制任务的提交流量,希望调用者自己处理被拒绝的任务的场景。
-
DiscardPolicy
当无法提交新任务时,抛弃策略会抛弃该任务。
适用场景:适用于对任务丢失不敏感的场景。
-
DiscardOldestPolicy
DiscardOldestPolicy抛弃最旧策略首先从队列头部移除一个任务,然后重新提交新任务加入队列。
适用场景:适用于对新任务优先级较高的场景,当线程池无法接受新任务时,会丢弃一些等待时间较长的旧任务,以便接受新任务。
如果要自定义拒绝策略,需要实现RejectedExecutionHandler接口。
class CustomRejectPolicy implements RejectedExecutionHandler {
private final BlockingQueue<Runnable> workQueue;
public CustomRejectPolicy(BlockingQueue<Runnable> workQueue) {
this.workQueue = workQueue;
}
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 将被拒绝的任务重新放入队列中等待执行
try {
workQueue.put(r);
} catch (InterruptedException e) {
//终断当前线程
Thread.currentThread().interrupt();
}
}
}