Java 并发编程学习(七):正确地创建线程池
线程池的正确创建方式
虽然Executors类中提供了许多的工厂方法来创建各种的线程池,但是在实际的生产环境中,却不推荐直接使用这些线程池。国内大厂阿里巴巴的Java开发指导手册就约束了这个行为。不推荐使用的原因主要是:
- Executors的静态方法提供的线程池默认使用无解的阻塞队列,如果提交的计算任务过多,有存在OOM的风险。
- 不能设置任务拒绝策略。
- 不能指定线程工厂ThreadFactory
在《Java并发编程实战》这本书中,作者描述了使用线程池的最佳实践:
-
使用ThreadPoolExecutor创建线程池对象
-
针对业务估算线程池中线程数量,线程数量计算公式如下:
-
使用自定义的ThreadFactory,为创建的线程设置有意义的命名。
-
使用有界队列
-
为线程设置未捕获的异常处理器
-
设置线程池的拒绝策略
下面的代码使用ThreadPoolExecutor的构造函数创建了自定义的线程池:
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) {
// 线程数量
int threadNum = Runtime.getRuntime().availableProcessors();
// 任务队列
BlockingQueue<Runnable> taskQueue = new ArrayBlockingQueue<>(1024);
// 自定义线程池工厂
CustomThreadFactory threadFactory = new CustomThreadFactory();
// 自定义拒绝策略
CustomRejectHandler rejectHandler = new CustomRejectHandler();
// 使用ThreadPoolExecutor构造线程池对象
ThreadPoolExecutor pool = new ThreadPoolExecutor(threadNum, threadNum, 5, TimeUnit.SECONDS, taskQueue, threadFactory, rejectHandler);
}
}
/**
* 自定义的拒绝处理器。当任务队列空间被占满时,再提交任务就执行拒绝逻辑。
*/
class CustomRejectHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 可以记录日志。或者将任务保存到其他地方,等任务队列有空时再执行。
System.out.println("自定义拒绝行为");
}
}
/**
* 自定义的线程工厂。线程池创建线程时会调用线程工厂来产生一个线程对象
*/
class CustomThreadFactory implements ThreadFactory {
private Thread.UncaughtExceptionHandler exceptionHandler = (thread, exception) -> {
// 在这里处理线程抛出的未捕获的异常
exception.printStackTrace();
};
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
// 为线程设置有意义的名称
t.setName("bizThread");
// 为线程设置未捕获的异常处理器
t.setUncaughtExceptionHandler(exceptionHandler);
return t;
}
}