1. yml属性配置
pojo:
web-config:
thread-pool:
corePoolSize: 100
maximumPoolSize: 200
keepAliveTime: 500
queueCapacity: 1000
threadNamePrefix: thead-test
rejectPolicy: callerRuns
workQueuePolicy: linked
2. prop属性pojo
@Data
@ConfigurationProperties(prefix = "pojo.web-config.thread-pool")
public class WebConfigThreadPollProp {
/** 核心基本数量(没有多线程任务,仍然保持该数量) */
private int corePoolSize;
/** 线程池最大能容忍的线程数 */
private int maximumPoolSize;
/** 线程存活时间 */
private long keepAliveTime;
/** 请求等待队列 */
private int queueCapacity;
/** 线程名前缀 */
private String threadNamePrefix;
/** 排队策略 */
private String workQueuePolicy;
/** 拒绝策略 */
private String rejectPolicy;
public interface QueuePolicyConstant{
/** 不缓存任务到队列,直接执行任务 */
String SYNCHRONOUS ="synchronous";
/** 基于数组的先进先出阻塞队列 */
String ARRAY = "array";
/** 基于链表的先进先出阻塞队列,默认大小为Integer.max */
String LINKED ="linked";
}
public interface RejectPolicyConstant{
/** 不在新线程中执行任务,而是有调用者所在的线程来执行 */
String CALLER_RUNS_POLICY = "callerRuns";
/** 抛出异常,拒绝执行 */
String ABORT_POLICY = "abort";
/** 丢弃任务但不抛出异常 */
String DISCARD_POLICY = "discard";
/** 丢弃队列最头部的 */
String DISCARD_OLDEST_POLICY = "discardOldest";
}
}
3. Configurable配置
@EnableAsync
@Configuration
@EnableConfigurationProperties(WeiareWebConfigThreadPollProp.class)
public class ThreadConfig implements AsyncConfigurer {
@Resource
private WebConfigThreadPollProp webConfigProp;
@Autowired
private SysOptLogger sysOptLogger;
@Override
@Bean(THREAD_POOL_EXECUTOR)
@ConditionalOnMissingBean(Executor.class)
public Executor getAsyncExecutor() {
BlockingQueue workQueue;
if (webConfigProp.getQueueCapacity() < 1) {
webConfigProp.setQueueCapacity(Integer.MAX_VALUE);
}
// 排队策略
switch (webConfigProp.getWorkQueuePolicy()) {
case ARRAY:
workQueue = new ArrayBlockingQueue<>(webConfigProp.getQueueCapacity());
break;
case LINKED:
workQueue = new LinkedBlockingQueue<>(webConfigProp.getQueueCapacity());
break;
case SYNCHRONOUS:
workQueue = new SynchronousQueue<>();
default:
workQueue = new ArrayBlockingQueue<>(webConfigProp.getQueueCapacity());
break;
}
// rejection-policy拒绝策略 :当pool已经达到max size的时候,如何处理新任务
RejectedExecutionHandler rejectedExecutionHandler;
switch (webConfigProp.getRejectPolicy()) {
case ABORT_POLICY:
rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
break;
case DISCARD_POLICY:
rejectedExecutionHandler = new ThreadPoolExecutor.DiscardPolicy();
break;
case DISCARD_OLDEST_POLICY:
rejectedExecutionHandler = new ThreadPoolExecutor.DiscardOldestPolicy();
break;
default:
rejectedExecutionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
break;
}
String threadName = webConfigProp.getThreadNamePrefix()+"-%d";
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat(threadName).build();
//todo 配置线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(webConfigProp.getCorePoolSize(), webConfigProp.getMaximumPoolSize(), webConfigProp.getKeepAliveTime(), TimeUnit.SECONDS, workQueue, namedThreadFactory, rejectedExecutionHandler);
//执行初始化
executor.prestartAllCoreThreads();
return executor;
}
@Override
@Bean
@ConditionalOnMissingBean(AsyncUncaughtExceptionHandler.class)
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler(){
//见4.持久化异步异常日志
}
}
4. 持久化异步异常日志
@Override
@Bean
@ConditionalOnMissingBean(AsyncUncaughtExceptionHandler.class)
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (throwable, method, objects) -> {
if (sysOptLogger != null) {
//持久化日志到本地数据库
// INSERT INTO SYS_OPT_LOG ( ID,DELETED,CREATE_TIME,MODIFIED_TIME,LOG_TYPE,TITLE,CHANNEL,ORDER_NO,CONTENT,STATUS,NOTIFY_DETAIL,NOTIFY_RESULT ) VALUES( ?,?,?,?,?,?,?,?,?,?,?,? )
// Parameters: CFCYX1K2W84G0WJRYDUZ(String), 0(Integer), 2018-10-19 11:43:03.513(Timestamp), 2018-10-19 11:43:03.513(Timestamp), 3(String), weiare-yc-0异步任务异常(String), 02(String), java.lang.NumberFormatException:For input string: "22s2"(String), 异步任务执行失败(String), 0(String), com.??.test.facade.YcThreadTestFacade.helloAsync(String), [](String)
//日志构建器建造并持久化异常日志
sysOptLogger.setHead(Thread.currentThread().getName() + "异步任务异常", SysOptLogConstant.LogType.RUNTIME_EXCEPTION, SysOptLogConstant.Channel.THREAD_POOL_EXCEPTION)
.setOrderNo(throwable.getClass().getName() + Symbol.COLON + throwable.getMessage())
.setNotify(method.getDeclaringClass().getName() + Symbol.DOT + method.getName(), JsonMapper.getMapper().toJson(objects))
.isSuccess(false, "异步任务执行失败")
.save();
}
};
}
5. 测试
@RequestMapping
@ResponseBody
public String test(){
ycThreadTestFacade.helloAsync();
return "hello";
}
@Async
public String helloAsync(){
System.out.println(StringFormat.formatBracket("这个任务是在线程{}中异步执行的",Thread.currentThread().getName()));
Integer.parseInt("22s2");
return "";
}
// 控制台打印 :这个任务是在线程thead-test-0中异步执行的"
// 查看数据库异步任务异常日志:
id | log_type | title | channel | info | content | is_success | detail | log_time |
---|---|---|---|---|---|---|---|---|
1 | 3 | thead-test-0异步任务异常 | 02 | java.lang.NumberFormatException:For input string: "22s2" | 异步任务执行失败 | 0 | com.??.test.facade.YcThreadTestFacade.helloAsync | 2018-10-19 11:43:00 |