Executors
中默认的线程工厂和拒绝策略过于简单,通常对用户不够友好
一、自定义ThreadFactory
对线程池创建的线程必须明确标识,为线程本身指定有意义的名称和相应的序号。
显示调用来源、线程的业务含义,有助于快速定位到死锁、
StackOverflowError
等问题
如图:
二、自定义RejectedExecutionHandler
拒绝策略应该考虑到业务场景,返回相应的提示或者友好地跳转。
在ThreadPoolExecutor
中提供了四个公开的内部静态类:
AbortPolicy(默认)
: 丢弃任务并抛出RejectedExecutionException
异常。DiscardPolicy
: 丢弃任务,但是不抛出异常,这是不推荐的做法。DiscardOldestPolicy
: 抛弃队列中等待最久的任务,然后把当前任务加入队列中。CallerRunsPolicy
: 调用任务的run()
方法绕过线程池直接执行。
友好的拒绝策略有:
(1)保存到数据库进行削峰填谷。在空闲时再提取出来执行
(2)转向某个提示页面
(3)打印日志
如图:
三、测试
如图:
结果图:
四、源码
// UserThreadFactory.java
public class UserThreadFactory implements ThreadFactory {
private final String namePrefix;
private final AtomicInteger nextId = new AtomicInteger(1);
// 定义线程组名称,在使用 jstack 来排查线程问题时,非常有帮助
UserThreadFactory(String whatFeatureOfGroup) {
this.namePrefix = "UserThreadFactory's " + whatFeatureOfGroup + "-Worker-";
}
public Thread newThread(Runnable runnable) {
String name = this.namePrefix + nextId.getAndIncrement();
Thread thread = new Thread(null, runnable, name, 0);
System.out.println(thread.getName());
return thread;
}
}
// 任务执行体
class Task implements Runnable {
private final AtomicLong count = new AtomicLong(0L);
public void run() {
System.out.println("running_" + count.getAndIncrement());
}
}
// UserRejectHandler.java
public class UserRejectHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable runnable, ThreadPoolExecutor threadPoolExecutor) {
System.out.println("task rejected. " + threadPoolExecutor.toString());
}
}
// UserThreadPool.java
public class UserThreadPool {
public static void main(String[] args) {
// 缓存队列设置固定长度为2, 为了快速触发 rejectHandler
BlockingDeque<Runnable> blockingDeque = new LinkedBlockingDeque<>(2);
// 假设外部任务线程的来源由机房1 和 机房2的混合调用
UserThreadFactory f1 = new UserThreadFactory(" 第 1 机房 ");
UserThreadFactory f2 = new UserThreadFactory(" 第 2 机房 ");
UserRejectHandler handler = new UserRejectHandler();
// 核心线程为1, 最大线程为2, 为了保证触发 rejectHandler
ThreadPoolExecutor threadPoolFirst = new ThreadPoolExecutor(1, 2,
60, TimeUnit.SECONDS, blockingDeque, f1, handler);
// 利用第二个线程工厂实例创建第二个线程池
ThreadPoolExecutor threadPoolSecond = new ThreadPoolExecutor(1, 2,
60, TimeUnit.SECONDS, blockingDeque, f2, handler);
// 创建 400 个任务线程
Runnable task = new Task();
for (int i = 0; i < 200; ++i) {
threadPoolFirst.execute(task);
threadPoolSecond.execute(task);
}
}
}