原因:由于线程不阻碍主线程,所以线程发生异常,除非在日志里查看或者线程方法有try catch处理,否则很难发现异常,所以需要个兜底的方法,而线程中有个new Thread().setUncaughtExceptionHandler();
可以帮助我们处理这种未捕获异常,而在线程池中,则需要我们重写new ThreadFactory,从线程工厂中,即 executor.setThreadFactory();这里可以将线程异常做兜底处理(发送邮件通知或者try catch)
以下代码
线程池
/**
* author chenkang
*/
@Configuration
@EnableAsync
@Log4j2
//注解线程池 使用时注意Async失效的情况
public class ThreadPoolConfig {
@Resource
private MyThreadFactory myThreadFactory;
//获取当前cpu 核数
public static final int cpuNum = Runtime.getRuntime().availableProcessors();
//一般线程池
@Bean("taskExecutor")
public Executor getAsyncExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//线程池核心线程数
executor.setCorePoolSize(cpuNum);
//线程池最大线程数
executor.setMaxPoolSize(cpuNum * 2);
//线程池队列大小
executor.setQueueCapacity(Runtime.getRuntime().availableProcessors()*10);
//线程活跃时间
executor.setKeepAliveSeconds(30000);
executor.setThreadFactory(myThreadFactory);
//线程名称前缀
executor.setThreadNamePrefix("sungrow-executor-");
//线程池拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
threadFactory
package org.jeecg.common.config;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.mail.MailAccount;
import cn.hutool.extra.mail.MailUtil;
import lombok.Data;
import org.jeecg.common.util.RedisProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.concurrent.ThreadFactory;
//自定义threadFactory
@Component
@Data
public class MyThreadFactory implements ThreadFactory {
@Autowired
private ThreadErrorModel threadErrorModel;
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setUncaughtExceptionHandler((t, e) -> {
String result = generateErrorMessage(e);
sendEmailMessage(result);
});
return thread;
}
/**
* 发送报错信息到开发邮箱
* @param result
*/
private void sendEmailMessage(String result) {
MailAccount account = new MailAccount();
account.setHost(threadErrorModel.getHost());
account.setPort(threadErrorModel.getPort());
account.setAuth(true);
account.setFrom(threadErrorModel.getFrom());
account.setUser(threadErrorModel.getUser());
account.setPass(threadErrorModel.getPass());
MailUtil.send(account,threadErrorModel.getEmails(), "慧碳平台线程错误,请开发协助调查", result, false);
}
/**
* 生产报错信息
* @param e
* @return result
*/
private String generateErrorMessage(Throwable e) {
StackTraceElement[] stackTraceElements = e.getStackTrace();
String result = e + "\n";
for (int index = stackTraceElements.length - 1; index >= 0; --index) {
result += "at [" + stackTraceElements[index].getClassName() + ",";
result += stackTraceElements[index].getFileName() + ",";
result += stackTraceElements[index].getMethodName() + ",";
result += stackTraceElements[index].getLineNumber() + "]\n";
}
return result;
}
}
配置发送邮件信息
package org.jeecg.common.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
@Data
@Configuration
@ConfigurationProperties(prefix = "thread.error.email")
public class ThreadErrorModel {
private String host;
private int port;
private String auth;
private String from;
private String user;
private String pass;
private List<String> emails;
}
yaml 配置文件
测试:
package org.jeecg.modules.system.controller;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.mail.MailAccount;
import cn.hutool.extra.mail.MailUtil;
import lombok.extern.log4j.Log4j2;
import org.jeecg.common.exception.JeecgBootException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.File;
import java.io.FileNotFoundException;
//线程池测试
@RestController
@RequestMapping("/test")
@Log4j2
public class TestThreadPoolController {
@Resource
private ThreadPoolTaskExecutor taskExecutor;
@GetMapping("/threadPool")
public void testThreadPool(){
taskExecutor.execute(()->{
log.info("=============="+Thread.currentThread().getName());
int a = 3/0;
});
log.info("kang异常");
}
}
产生异常但并不阻碍主线程,如果不是一直盯着日志并且没有try catch异常,则很难发现异常
查看邮件 收到异常邮件,开发运维可以排除异常