在Java并发编程中,线程池是一个非常重要的工具,它能够帮助我们有效地管理和复用线程资源。本文将深入探讨线程池的高级使用方式,包括自定义线程池、动态调整策略、监控与优化等方面。
1. 自定义线程工厂
默认的线程工厂虽然能满足基本需求,但在生产环境中,我们往往需要更多的控制和监控能力。以下是一个自定义线程工厂的示例:
java
public class CustomThreadFactory implements ThreadFactory {
private final String namePrefix;
private final AtomicInteger threadNumber = new AtomicInteger(1);
public CustomThreadFactory(String poolName) {
this.namePrefix = poolName + "-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, namePrefix + threadNumber.getAndIncrement());
// 设置为守护线程
thread.setDaemon(true);
// 设置线程优先级
thread.setPriority(Thread.NORM_PRIORITY);
// 设置异常处理器
thread.setUncaughtExceptionHandler((t, e) -> {
System.err.println("Thread " + t.getName() + " threw exception: " + e.getMessage());
e.printStackTrace();
});
return thread;
}
}
2. 动态调整线程池
在实际应用中,我们可能需要根据系统负载动态调整线程池的参数。以下是一个示例实现:
java
public class DynamicThreadPool extends ThreadPoolExecutor {
private final ReentrantLock lock = new ReentrantLock();
public DynamicThreadPool(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
public void adjustThreadPool(int corePoolSize, int maximumPoolSize) {
lock.lock();
try {
if (corePoolSize > maximumPoolSize) {
throw new IllegalArgumentException("Core pool size cannot be larger than maximum pool size");
}
super.setCorePoolSize(corePoolSize);
super.setMaximumPoolSize(maximumPoolSize);
} finally {
lock.unlock();
}
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
// 任务执行前的钩子
super.beforeExecute(t, r);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
// 任务执行后的钩子
super.afterExecute(r, t);
}
}
3. 任务优先级队列
有时我们需要为任务设置优先级,以下是一个支持优先级的任务队列实现:
java
public class PriorityTask implements Runnable, Comparable<PriorityTask> {
private final int priority;
private final Runnable task;
public PriorityTask(int priority, Runnable task) {
this.priority = priority;
this.task = task;
}
@Override
public void run() {
task.run();
}
@Override
public int compareTo(PriorityTask other) {
return Integer.compare(other.priority, this.priority); // 优先级高的先执行
}
}
// 使用示例
PriorityBlockingQueue<Runnable> queue = new PriorityBlockingQueue<>();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, 10, 60L, TimeUnit.SECONDS, queue,
new CustomThreadFactory("priority-pool")
);
4. 线程池监控
监控线程池的运行状况对于性能优化和问题排查非常重要:
java
public class ThreadPoolMonitor {
private final ThreadPoolExecutor executor;
private final AtomicLong totalTasks = new AtomicLong();
private final AtomicLong completedTasks = new AtomicLong();
public ThreadPoolMonitor(ThreadPoolExecutor executor) {
this.executor = executor;
}
public void startMonitoring() {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> {
System.out.println("==== Thread Pool Status ====");
System.out.println("Active Threads: " + executor.getActiveCount());
System.out.println("Core Pool Size: " + executor.getCorePoolSize());
System.out.println("Maximum Pool Size: " + executor.getMaximumPoolSize());
System.out.println("Task Count: " + executor.getTaskCount());
System.out.println("Completed Tasks: " + executor.getCompletedTaskCount());
System.out.println("Queue Size: " + executor.getQueue().size());
}, 0, 1, TimeUnit.MINUTES);
}
}
5. 使用最佳实践
-
合理配置线程池参数
- 核心线程数建议设置为CPU核心数+1
- 最大线程数建议设置为CPU核心数*2
- 任务队列大小要根据实际业务场景评估
-
任务分类处理
- IO密集型任务可以配置较多的线程数
- CPU密集型任务线程数不应超过CPU核心数
- 对于不同类型的任务应该使用不同的线程池
-
异常处理
- 为线程池中的任务添加try-catch块
- 实现UncaughtExceptionHandler处理未捕获的异常
- 使用Future获取任务执行结果时要处理ExecutionException
-
优雅关闭
java
public static void shutdownThreadPoolGracefully(ThreadPoolExecutor executor) {
executor.shutdown(); // 停止接收新任务
try {
// 等待已提交任务执行完成
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 强制终止
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("Thread pool did not terminate");
}
}
} catch (InterruptedException ie) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
总结
线程池的高级使用需要我们对Java并发编程有深入的理解。通过合理使用自定义线程工厂、动态调整策略、优先级队列等特性,可以让线程池更好地服务于我们的业务需求。同时,做好监控和异常处理也是不可或缺的部分。在实际应用中,需要根据具体场景选择合适的特性和配置,并持续优化以达到最佳效果。