java线程池通过复用线程提升性能和稳定性,核心是threadpoolexecutor,其参数需根据业务类型精细配置,避免使用executors的默认方法以防oom;1. corepoolsize和maximumpoolsize应依据cpu密集型(通常设为cpu核数或加1)或i/o密集型(可设为cpu核数×(1+阻塞系数))任务合理设置;2. workqueue推荐使用有界队列如arrayblockingqueue防止内存溢出,避免无界队列导致oom;3. 拒绝策略应根据业务需求选择abortpolicy、callerrunspolicy等,或自定义处理;4. keepalivetime用于回收多余空闲线程,i/o密集型可适当缩短;任务提交可通过execute(无返回值)、submit(返回future获取结果或异常)、invokeall(等待所有任务完成)和invokeany(任一任务完成即返回)实现;关闭线程池需先调用shutdown()拒绝新任务并等待完成,再通过awaittermination等待终止,超时则调用shutdownnow()强制关闭,并处理interruptedexception,确保资源释放和任务完整性,防止线程泄露或任务丢失。

Java线程池,说白了,就是一套聪明地管理线程的机制。它通过复用已创建的线程,避免了频繁创建和销毁线程带来的性能损耗,同时还能有效地控制并发数量,防止系统资源耗尽,从而显著提升应用的响应速度和整体稳定性。这玩意儿,在高性能和高并发场景下,简直是基石一般的存在。
要使用Java线程池,我们通常会接触到
java.util.concurrent
Executor
ExecutorService
ThreadPoolExecutor
Executors
ThreadPoolExecutor
一个典型的
ThreadPoolExecutor
立即学习“Java免费学习笔记(深入)”;
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)这里面每个参数都至关重要:
corePoolSize
maximumPoolSize
keepAliveTime
unit
corePoolSize
workQueue
threadFactory
handler
实际应用中,你可能会这样创建一个线程池:
import java.util.concurrent.*;
public class MyThreadPool {
public static void main(String[] args) {
// 创建一个自定义的线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(100), // 任务队列,容量100
Executors.defaultThreadFactory(), // 默认线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略:直接抛出异常
);
// 提交任务
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Executing task " + taskId + " by " + Thread.currentThread().getName());
try {
Thread.sleep(100); // 模拟任务执行
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 关闭线程池
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 强制关闭
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}说实话,
Executors
newFixedThreadPool
newCachedThreadPool
举个例子,
newFixedThreadPool
LinkedBlockingQueue
再比如
newCachedThreadPool
SynchronousQueue
maximumPoolSize
Integer.MAX_VALUE
所以,你看,这些默认的工厂方法,虽然用起来简单,但它们隐藏了关键的配置细节,让开发者失去了对线程数量和任务队列容量的控制权。在实际项目中,我们必须对这些核心参数有清晰的认知和合理的规划,否则,埋下的隐患迟早会爆发。直接使用
ThreadPoolExecutor
配置
ThreadPoolExecutor
1. corePoolSize
maximumPoolSize
corePoolSize
maximumPoolSize
corePoolSize
corePoolSize
maximumPoolSize
2. workQueue
选择合适的任务队列也至关重要,它决定了任务的缓冲策略。
ArrayBlockingQueue
LinkedBlockingQueue
Executors.newFixedThreadPool
SynchronousQueue
Executors.newCachedThreadPool
PriorityBlockingQueue
Comparable
Comparator
3. RejectedExecutionHandler
当线程池和工作队列都满了,新任务来了怎么办?拒绝策略决定了它的命运。
ThreadPoolExecutor.AbortPolicy
RejectedExecutionException
ThreadPoolExecutor.CallerRunsPolicy
execute
submit
ThreadPoolExecutor.DiscardPolicy
ThreadPoolExecutor.DiscardOldestPolicy
RejectedExecutionHandler
4. keepAliveTime
这个参数决定了当线程池中的线程数量超过
corePoolSize
总的来说,配置线程池参数是一个不断尝试和优化的过程。通常的流程是:根据业务类型(CPU/I/O密集)初步估算参数,然后通过压力测试、监控线程池状态(如队列长度、活跃线程数)来观察其表现,最后根据实际运行情况进行微调。
把任务扔进线程池,并拿到结果,这事儿有几种不同的“姿势”,每种都有它适用的场景。
1. execute(Runnable task)
这是最基础的任务提交方式。你给它一个
Runnable
Runnable
run()
void
execute
Runnable
UncaughtExceptionHandler
execute
executor.execute(() -> {
System.out.println("Executing a simple runnable task.");
// 假设这里可能抛出异常
// int i = 1 / 0;
});2. submit(Runnable task)
submit(Callable<T> task)
submit
execute
Future
submit(Runnable task)
Runnable
Future
Future.get()
ExecutionException
Future<?> future = executor.submit(() -> {
System.out.println("Runnable task submitted, check future.");
// int i = 1 / 0; // 这里的异常会被Future.get()捕获
});
try {
future.get(); // 阻塞直到任务完成,如果任务有异常会在这里抛出ExecutionException
System.out.println("Runnable task completed successfully.");
} catch (InterruptedException | ExecutionException e) {
System.err.println("Runnable task failed: " + e.getMessage());
}submit(Callable<T> task)
Callable
Callable
call()
T
Future.get()
ExecutionException
Future<String> resultFuture = executor.submit(() -> {
System.out.println("Callable task is running...");
Thread.sleep(200);
// if (Math.random() > 0.5) throw new Exception("Random error!");
return "Task result: " + System.currentTimeMillis();
});
try {
String result = resultFuture.get(); // 阻塞并获取结果
System.out.println("Callable task completed with result: " + result);
} catch (InterruptedException | ExecutionException e) {
System.err.println("Callable task failed: " + e.getMessage());
if (e.getCause() != null) {
System.err.println("Original cause: " + e.getCause().getMessage());
}
}3. invokeAll(Collection<? extends Callable<T>> tasks)
当你有一批独立的
Callable
invokeAll
Future
Future
import java.util.ArrayList;
import java.util.List;
List<Callable<String>> callables = new ArrayList<>();
for (int i = 0; i < 3; i++) {
final int taskId = i;
callables.add(() -> {
System.out.println("Invoking task " + taskId);
Thread.sleep(500 - taskId * 100); // 模拟不同耗时
return "Result from task " + taskId;
});
}
try {
List<Future<String>> futures = executor.invokeAll(callables);
for (Future<String> f : futures) {
System.out.println(f.get()); // 逐个获取结果
}
} catch (InterruptedException | ExecutionException e) {
System.err.println("InvokeAll failed: " + e.getMessage());
}4. invokeAny(Collection<? extends Callable<T>> tasks)
与
invokeAll
invokeAny
List<Callable<String>> fastCallables = new ArrayList<>();
fastCallables.add(() -> { Thread.sleep(2000); return "Slow task result"; });
fastCallables.add(() -> { Thread.sleep(500); return "Fast task result"; });
fastCallables.add(() -> { Thread.sleep(1000); return "Medium task result"; });
try {
String fastestResult = executor.invokeAny(fastCallables);
System.out.println("Fastest result: " + fastestResult);
} catch (InterruptedException | ExecutionException e) {
System.err.println("InvokeAny failed: " + e.getMessage());
}在实际开发中,
submit(Callable)
Future
Future
isDone()
isCancelled()
cancel()
Future.get()
CompletableFuture
线程池用完了,不是简单地让程序退出就完事儿了。正确地关闭线程池,是避免资源泄露、确保所有任务妥善处理的关键。这里面也有一些“讲究”。
1. shutdown()
这是最常用的关闭方式。调用
shutdown()
executor.shutdown(); // 告诉线程池:我不再提交新任务了
2. shutdownNow()
这个方法更“暴力”一些。它会尝试停止所有正在执行的任务,并清空任务队列中所有等待的任务。它会返回一个
List<Runnable>
List<Runnable> unexecutedTasks = executor.shutdownNow(); // 尝试立即停止所有任务
System.out.println("Unexecuted tasks: " + unexecutedTasks.size());注意,
shutdownNow()
Thread.interrupt()
while(true)
Thread.currentThread().isInterrupted()
3. awaitTermination(long timeout, TimeUnit unit)
光调用
shutdown()
shutdown()
awaitTermination()
executor.shutdown(); // 发出关闭信号
try {
// 等待所有任务在指定时间内完成,如果超时则返回false
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("线程池未在指定时间内终止,尝试强制关闭...");
executor.shutdownNow(); // 如果超时了,就强制关闭
// 再次等待,确保强制关闭成功
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("线程池未能完全终止!");
}
}
} catch (InterruptedException e) {
// 当前线程在等待时被中断
executor.shutdownNow(); // 强制关闭
Thread.currentThread().interrupt(); // 重新设置中断状态
}
System.out.println("线程池已关闭。");常见陷阱:
shutdown()
shutdownNow()
shutdownNow()
shutdownNow()
shutdown()
shutdownNow()
InterruptedException
awaitTermination()
InterruptedException
executor.shutdownNow()
Thread.currentThread().interrupt()
Thread.sleep()
Object.wait()
shutdownNow()
以上就是java如何使用线程池管理线程资源 java线程池应用的实用技巧指南的详细内容,更多请关注php中文网其它相关文章!
java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号