深入理解Java线程池
Java线程池是Java并发编程中的一个重要概念,它提供了一种管理和复用线程的机制,可以显著减少创建和销毁线程的开销,提高系统的响应速度和吞吐量。以下是对Java线程池的详细解析:
一、线程池的基本概念
线程池是一个由多个线程组成的集合,这些线程可以被动态地分配给不同的任务。线程池中的线程在执行完一个任务后,并不会被销毁,而是会回到线程池中等待执行下一个任务。这样可以避免频繁地创建和销毁线程,提高系统的性能。
二、线程池的核心参数
- corePoolSize(核心线程数):线程池中始终保持的线程数量,即使这些线程处于空闲状态。
- maximumPoolSize(最大线程数) :线程池能够容纳的最大线程数。当工作队列满时,线程池会尝试创建新的线程,直到线程数量达到
maximumPoolSize
。 - keepAliveTime(线程空闲时间) :当线程数量超过
corePoolSize
时,这是多余空闲线程在终止前等待新任务的最长时间。 - unit(时间单位) :用于指定
keepAliveTime
的时间单位。 - workQueue(任务队列):用于保存等待执行的任务的阻塞队列。
- threadFactory(线程工厂):用于创建新线程的工厂。通过线程工厂可以给每个创建出来的线程设置更有意义的名字。
- handler(拒绝策略) :当队列和线程池都满了,说明线程池处于饱和状态,必须采取一种策略处理提交的新任务。这个策略默认情况下是
AbortPolicy
,表示无法处理新任务时抛出异常。还可以选择CallerRunsPolicy
(用调用者所在线程来运行任务)、DiscardPolicy
(丢弃队列里最近的一个任务,并执行当前任务)、DiscardOldestPolicy
(丢弃最老的任务,尝试重新提交新的任务)等策略,或者根据应用场景实现RejectedExecutionHandler
接口自定义策略。
三、线程池的工作流程
- 提交任务 :任务通过
execute(Runnable command)
或submit(Callable<T> task)
方法提交到线程池。 - 判断核心线程是否空闲 :如果线程池中的核心线程数量少于
corePoolSize
,则创建新的核心线程来执行任务。 - 放入任务队列 :如果核心线程都在忙,则任务会被放入
workQueue
中等待执行。 - 创建非核心线程 :如果
workQueue
已满,且当前线程数量少于maximumPoolSize
,则线程池会尝试创建新的非核心线程来执行任务。 - 拒绝任务 :如果
workQueue
已满,且线程数量已达到maximumPoolSize
,则线程池会根据拒绝策略拒绝新任务。 - 线程回收 :当线程数量超过
corePoolSize
,且空闲线程的空闲时间超过keepAliveTime
,则这些线程会被回收。
四、常见的线程池类型
- FixedThreadPool(固定大小线程池):包含固定数量的线程,适用于需要限制并发线程数量的场景。
- CachedThreadPool(缓存线程池):不固定线程数量,可以根据需要自动创建新线程,适用于执行大量短期异步任务。
- SingleThreadPool(单线程池):只包含一个工作线程,保证所有任务按顺序执行,适用于需要保持任务顺序执行的场景。
- ScheduledThreadPool(定时线程池):可以执行定时任务和周期性任务。
- WorkStealingPool(工作窃取线程池):Java 8中引入的一种新类型的线程池,主要用于处理耗时任务,适用于需要大量并行任务、任务之间没有依赖关系的情况。
五、线程池的使用方法和注意事项
- 创建线程池 :可以使用
ThreadPoolExecutor
类来创建线程池,并设置相应的参数。也可以使用Executors
工厂类来方便地创建常见的线程池类型。 - 提交任务 :通过
execute()
或submit()
方法将任务提交到线程池。 - 关闭线程池 :线程池应该通过
shutdown()
或shutdownNow()
方法显式关闭。shutdown()
方法会平滑地关闭线程池,不再接受新任务,但会继续执行队列中的任务。shutdownNow()
方法会尝试立即关闭线程池,并尝试停止正在执行的任务。 - 注意事项 :
- 避免创建过多的线程,合理配置线程池的大小。
- 任务执行时间不宜过长,以免影响线程池的响应速度。
- 确保提交给线程池的任务是线程安全的。
- 根据应用需求合理设置线程池的参数。
六、示例代码
使用ThreadPoolExecutor
创建线程池并提交任务的示例代码:
java
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
60L, TimeUnit.SECONDS, // 线程空闲时间和时间单位
new LinkedBlockingQueue<Runnable>()
);
// 向线程池提交任务
for (int i = 0; i < 10; i++) {
final int task = i;
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 正在执行第 " + (task + 1) + " 个任务");
}
});
}
// 关闭线程池
executor.shutdown();
}
}
使用Executors
工厂类创建不同类型线程池
java
import java.util.concurrent.*;
public class ExecutorsExample {
public static void main(String[] args) throws InterruptedException {
// 创建一个固定大小的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
// 创建一个缓存线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 创建一个单线程线程池
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
// 创建一个定时线程池
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
// 创建一个工作窃取线程池
ExecutorService workStealingPool = Executors.newWorkStealingPool();
// 提交任务给不同类型的线程池
for (int i = 0; i < 5; i++) {
final int taskIndex = i;
fixedThreadPool.submit(() -> printTaskInfo("Fixed", taskIndex));
cachedThreadPool.submit(() -> printTaskInfo("Cached", taskIndex));
singleThreadPool.submit(() -> printTaskInfo("Single", taskIndex));
scheduledThreadPool.schedule(() -> printTaskInfo("Scheduled", taskIndex), 2, TimeUnit.SECONDS); // 延迟2秒执行
}
// 关闭所有线程池
fixedThreadPool.shutdown();
cachedThreadPool.shutdown();
singleThreadPool.shutdown();
scheduledThreadPool.shutdown();
workStealingPool.shutdown();
}
private static void printTaskInfo(String poolType, int taskIndex) {
System.out.println(poolType + " Thread Pool: " + Thread.currentThread().getName() + " is executing Task-" + taskIndex);
}
}
使用自定义线程工厂和拒绝策略
java
import java.util.concurrent.*;
public class CustomThreadPoolExample {
public static void main(String[] args) {
// 定义自定义线程工厂
ThreadFactory namedThreadFactory = new ThreadFactory() {
private int count = 0;
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "MyCustomThread-" + count++);
}
};
// 定义自定义拒绝策略
RejectedExecutionHandler customRejectedHandler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.err.println("Task " + r.toString() + " rejected from " + executor.toString());
}
};
// 创建带有自定义线程工厂和拒绝策略的线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
namedThreadFactory,
customRejectedHandler
);
// 提交大量任务测试自定义拒绝策略
for (int i = 0; i < 20; i++) {
final int taskNumber = i;
executor.execute(() -> printTaskInfo("Custom", taskNumber));
}
// 关闭线程池
executor.shutdown();
}
private static void printTaskInfo(String poolType, int taskIndex) {
System.out.println(poolType + " Thread Pool: " + Thread.currentThread().getName() + " is executing Task-" + taskIndex);
}
}
使用submit()
方法提交可返回结果的任务
java
import java.util.concurrent.*;
public class CallableExample {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 创建线程池
ExecutorService executor = Executors.newSingleThreadExecutor();
// 提交Callable任务,该任务可以返回结果
Future<Integer> future = executor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// 模拟耗时操作
TimeUnit.SECONDS.sleep(2);
return 123;
}
});
// 获取任务的结果
try {
Integer result = future.get(); // 阻塞直到任务完成
System.out.println("Task returned: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
// 关闭线程池
executor.shutdown();
}
}
}
使用invokeAll()
方法提交多个任务并获取结果
java
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class InvokeAllExample {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 创建多个Callable任务
List<Callable<String>> tasks = new ArrayList<>();
for (int i = 0; i < 5; i++) {
final int taskId = i;
tasks.add(() -> {
// 模拟耗时操作
TimeUnit.SECONDS.sleep(1);
return "Result of Task-" + taskId;
});
}
// 提交所有任务并等待所有任务完成
List<Future<String>> futures = executor.invokeAll(tasks);
// 获取所有任务的结果
for (Future<String> future : futures) {
System.out.println(future.get()); // 阻塞直到每个任务完成
}
// 关闭线程池
executor.shutdown();
}
}
以上就是对Java线程池的详细解析,包括线程池的基本概念、核心参数、工作流程、常见类型、使用方法和注意事项等内容。通过深入理解线程池,我们可以更好地利用线程池来提高并发应用的性能和可靠性。