初始化线程池主要有两种常见的方式,分别是使用 Executors
工具类和手动创建 ThreadPoolExecutor
实例。
使用 Executors
工具类
Executors
是 Java 提供的一个线程池工具类,它提供了一些静态工厂方法来创建不同类型的线程池,使用起来较为方便。
1. 创建固定大小的线程池
使用 Executors.newFixedThreadPool(int nThreads)
方法可以创建一个固定大小的线程池,线程池中的线程数量始终保持为指定的大小。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPoolExample {
public static void main(String[] args) {
// 创建一个包含 3 个线程的固定大小线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 提交任务到线程池
for (int i = 0; i < 5; i++) {
final int taskId = i;
executorService.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task " + taskId + " is completed.");
});
}
// 关闭线程池
executorService.shutdown();
}
}
2. 创建单线程线程池
使用 Executors.newSingleThreadExecutor()
方法可以创建一个只有一个线程的线程池,所有任务会按照提交的顺序依次执行。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SingleThreadExecutorExample {
public static void main(String[] args) {
// 创建单线程线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
// 提交任务到线程池
for (int i = 0; i < 5; i++) {
final int taskId = i;
executorService.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task " + taskId + " is completed.");
});
}
// 关闭线程池
executorService.shutdown();
}
}
3. 创建可缓存的线程池
使用 Executors.newCachedThreadPool()
方法可以创建一个可缓存的线程池,线程池中的线程数量会根据任务的数量动态调整。如果线程池中的线程在 60 秒内没有被使用,会被自动回收。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPoolExample {
public static void main(String[] args) {
// 创建可缓存的线程池
ExecutorService executorService = Executors.newCachedThreadPool();
// 提交任务到线程池
for (int i = 0; i < 10; i++) {
final int taskId = i;
executorService.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task " + taskId + " is completed.");
});
}
// 关闭线程池
executorService.shutdown();
}
}
使用 Executors
工具类的优缺点
- 优点:使用简单,代码量少,适合快速开发和一些简单的场景。
- 缺点:可能会导致资源耗尽问题。例如,
newCachedThreadPool
允许创建的线程数量没有上限,在任务数量过多时可能会创建大量线程,导致系统资源耗尽;newFixedThreadPool
和newSingleThreadExecutor
使用的是无界队列,当任务提交速度过快时,队列可能会无限增长,也会导致内存溢出。
手动创建 ThreadPoolExecutor
实例
为了避免 Executors
工具类可能带来的资源耗尽问题,建议手动创建 ThreadPoolExecutor
实例,通过自定义线程池的参数来更好地控制线程池的行为。
import java.util.concurrent.*;
public class ThreadPoolExecutorExample {
public static void main(String[] args) {
// 核心线程数
int corePoolSize = 2;
// 最大线程数
int maximumPoolSize = 5;
// 线程空闲时间
long keepAliveTime = 60;
// 时间单位
TimeUnit unit = TimeUnit.SECONDS;
// 任务队列
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(10);
// 线程工厂
ThreadFactory threadFactory = Executors.defaultThreadFactory();
// 拒绝策略
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
// 创建线程池
ThreadPoolExecutor executorService = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);
// 提交任务到线程池
for (int i = 0; i < 20; i++) {
final int taskId = i;
executorService.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task " + taskId + " is completed.");
});
}
// 关闭线程池
executorService.shutdown();
}
}
手动创建 ThreadPoolExecutor
实例的优点
- 可以根据具体的业务需求灵活配置线程池的参数,如核心线程数、最大线程数、任务队列、线程空闲时间、线程工厂和拒绝策略等,避免资源耗尽问题,提高系统的稳定性和性能。
建议优先使用手动创建 ThreadPoolExecutor
实例的方式来初始化线程池。