SpringBoot实现多线程
多线程方式一:实现AsyncConfigurer 接口
配置类实现接口AsyncConfigurer,返回一个ThreadPoolTaskExecutor 线程池对象。
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
// ThredPoolTaskExcutor的处理流程
// 当池子大小小于corePoolSize,就新建线程,并处理请求
// 当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去workQueue中取任务并处理
// 当workQueue放不下任务时,就新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize,就用RejectedExecutionHandler来做拒绝处理
// 当池子的线程数大于corePoolSize时,多余的线程会等待keepAliveTime长时间,如果无请求可处理就自行销毁
@Override
@Bean
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数:线程池创建的时候初始化的线程数
executor.setCorePoolSize(10);
// 最大线程数:线程池最大的线程数,只有缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(100);
// 缓冲队列:用来缓冲执行任务的队列
executor.setQueueCapacity(50);
// 线程池关闭:等待所有任务都完成再关闭
executor.setWaitForTasksToCompleteOnShutdown(true);
// 等待时间:等待5秒后强制停止
executor.setAwaitTerminationSeconds(5);
// 允许空闲时间:超过核心线程之外的线程到达60秒后会被销毁
executor.setKeepAliveSeconds(60);
// 线程名称前缀
executor.setThreadNamePrefix("learn-Async-");
// 初始化线程
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
通过@Async注解表明该方法是异步方法,如果注解在类上,那表明这个类里面的所有方法都是异步的。
@Service
public class AsyncService {
@Async // 表明该方法是异步方法。如果注解在类上,那表明类里面的所有方法都是异步
public void executeAsyncTask(int i) {
System.out.println("线程" + Thread.currentThread().getName() + " 执行异步任务:" + i);
}
}
多线程方式二:配置 java.util.concurrent.TaskExecutor
新增一个配置类,默认情况下使用 SimpleAsyncTaskExecutor
@Configuration
@EnableAsync //启用异步任务
public class ThreadConfig {
@Bean("asyncServiceExecutor")
public ThreadPoolTaskExecutor executor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(15);
//配置最大线程数
executor.setMaxPoolSize(30);
//配置队列大小
executor.setQueueCapacity(1000);
//线程的名称前缀
executor.setThreadNamePrefix("Executor-");
//线程活跃时间(秒)
//executor.setKeepAliveSeconds(60);
//等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
//设置拒绝策略
//executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
使用线程池:可以使用@Async注解或者直接调用TaskExecutor的submit或者execute方法提交任务。
//-----------------------接口类--------------------------
public interface UserService{
/**
* 执行异步任务
*/
void writeText();
}
//-----------------------接口实现类----------------------
@Service
public class UserServiceImpl implement UserService{
private static Logger logger = LogManager.getLogger(AsyncServiceImpl.class.getName());
@Async("asyncServiceExecutor")
@Over
public void writeTxt(String fileName){
logger.info("线程-" + Thread.currentThread().getId() + "在执行写入");
try {
File file = new File(fileName);
List<String> lines = FileUtils.readLines(file);
File copyFile = new File(fileName + "_copy.txt");
lines.stream().forEach(string->{
try {
FileUtils.writeStringToFile(copyFile,string,"utf8",true);
FileUtils.writeStringToFile(copyFile,"\r\n","utf8",true);
} catch (IOException e) {
logger.info(e.getMessage());
}
});
}catch (Exception e) {
logger.info(e.getMessage());
}
}
}
//-----------------------测试----------------------------
@RunWith(SpringRunner.class)
@SpringBootTest
public class BootApplicationTests {
@Autowired
private AsyncService asyncService;
@Test
public void write() {
File file = new File("F://ac_code_1//test.txt");
try {
FileUtils.writeStringToFile(file, "ceshi", "utf8");
FileUtils.writeStringToFile(file, "\r\n", "utf8");
FileUtils.writeStringToFile(file, "ceshi2", "utf8");
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Async原理
本质上Async注解的实现依赖于动态代理,代理过程中将任务提交给线程池,存在以下流程:
如上图所示,Spring为使用Async注解Bean对象生产一个动态代理对象,当Async注解的方法被调用时,会进入代理流程,选择线程池、封装调用逻辑并提交给线程池执行,返回执行结果。
注意:未被@Async注解的方法则不会执行上述流程(static方法也不会)
由于异步的本质是基于代理实现,所以同一个类中的方法调用会导致被调用方的异步作用失效,该场景与Spring的事务失效原因相同。
注解@Async失效的情况
- 注解@Async的方法不是public方法
- 注解@Async的返回值只能为void或Future
- 注解@Async方法使用static修饰也会失效
- spring无法扫描到异步类,没加注解@Async或@EnableAsync注解
- 调用方与被调用方不能在同一个类(因为没走代理)
- 类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
- 在Async方法上标注@Transactional是没用的,但在Async方法调用的方法上标注@Transcational是有效的