Spring的@Async异步编程
使用@Async注解时,推荐使用自定义线程池的模式;查看源码,@Async的默认线程池为SimpleAsyncTaskExecutor,默认线程池有如下弊端:
在线程池应用中,参考阿里巴巴java开发规范:线程池不允许使用Executors去创建,不允许使用系统默认的线程池,推荐通过ThreadPoolExecutor的方式,这样的处理方式让开发的工程师更加明确线程池的运行规则,规避资源耗尽的风险。
一、自定义异步线程池
package com.nijia.base.config;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import lombok.extern.slf4j.Slf4j;
/**
* 自定义线程池<br/><br/>
*
* 使用@Async注解时,推荐使用自定义线程池的模式
* @author Lynch
*
*/
@EnableAsync
@Configuration
@Slf4j
public class TaskPoolConfig {
/**
* 核心线程数 自行设置
*/
private static final int CORE_POOL_SIZE = 50;
/**
* 最大线程数 自行设置
*/
private static final int MAX_POOL_SIZE = CORE_POOL_SIZE * 2 + 1;
@Bean("taskExecutor")
public Executor taskExecutor() {
//返回可用处理器的Java虚拟机的数量 16
int i = Runtime.getRuntime().availableProcessors();
log.info("系统最大线程数 : " + i);
// ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
MyThreadPoolTaskExecutor executor = new MyThreadPoolTaskExecutor();
//设置核心线程数
executor.setCorePoolSize(CORE_POOL_SIZE);
//设置最大线程数
executor.setMaxPoolSize(MAX_POOL_SIZE);
//配置队列容量,默认值为Integer.MAX_VALUE
executor.setQueueCapacity(99999);
//除核心线程外的线程存活时间
executor.setKeepAliveSeconds(3);
//线程名字前缀
executor.setThreadNamePrefix("thread-execute");
// 设置线程池中 任务的等待时间,如果超过这个时间还没有销毁就 强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
executor.setAwaitTerminationSeconds(60);
//优雅地关闭线程池,等待所有的任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
//设置拒绝策略
//CallerRunsPolicy():交由调用方线程运行,比如 main 线程;如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行
//AbortPolicy():该策略是线程池的默认策略,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。
//DiscardPolicy():如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常
//DiscardOldestPolicy():丢弃队列中最老的任务,队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
二、AsyncService
package cn.itcast.user.service;
/**
* 异步发送短信/邮件
*
* @author Lynch
*/
public interface AsyncService {
/**
* 发送短信
*
* @author Lynch
*/
void sendSms();
/**
* 发送邮件
*
* @author Lynch
*/
void sendEmail();
}
三、使用@Async注解异步执行——AsyncServiceImpl
package cn.itcast.user.service.impl;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import cn.itcast.user.service.AsyncService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {
@Override
@Async("taskExecutor")
public void sendSms() {
try {
Thread.sleep(3000);
log.info("发送短信成功");
} catch (Exception e) {
log.error("发送短信异常 -> ", e);
}
}
@Override
@Async("taskExecutor")
public void sendEmail() {
try {
Thread.sleep(2000);
log.info("发送email成功");
} catch (Exception e) {
log.error("发送email异常 -> ", e);
}
}
}
四、单元测试——AsyncServiceTest
package cn.itcast.user.service;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import cn.itcast.user.BaseTest;
public class AsyncServiceTest extends BaseTest {
@Autowired
private AsyncService asyncService;
@Test
public void send() {
long begin = System.currentTimeMillis();
asyncService.sendSms();
asyncService.sendEmail();
System.out.println("执行耗时:" + (System.currentTimeMillis() - begin) + "毫秒");
}
}
使用方法二:
package com.nijia.user.controller;
@RestController
@RequestMapping("/open")
public class UserDemoController {
@Autowired
private Executor taskExecutor;
@GetMapping("executor")
public String executor() throws Exception {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(400L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "111";
}, taskExecutor);
if(taskExecutor instanceof MyThreadPoolTaskExecutor) {
System.out.println(((MyThreadPoolTaskExecutor) taskExecutor).getPoolSize());
System.out.println(((MyThreadPoolTaskExecutor) taskExecutor).getMaxPoolSize());
System.out.println(((MyThreadPoolTaskExecutor) taskExecutor).getCorePoolSize());
}
return future.get();
}
}