1. 异步任务使用
(1)创建线程池配置
@Configuration
@EnableAsync //开启多线程
public class ThreadPoolConfig {
@Bean("taskExecutor")
public Executor asyncServiceExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//设置核心线程数
executor.setCorePoolSize(5);
//设置最大线程数
executor.setMaxPoolSize(20);
//配置队列大小
executor.setQueueCapacity(Integer.MAX_VALUE);
//设置线程活跃时间
executor.setKeepAliveSeconds(60);
//设置线程名称
executor.setThreadNamePrefix("lewang");
//等待所有线程执行完成后关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
//执行初始化
executor.initialize();
return executor;
}
}
(2)在查看文章时对更新操作使用异步
/*
文章详情
*/
@PostMapping("/view/{id}")
public Result findArticleById(@PathVariable("id") Long articleId){
return articleService.findArticleById(articleId);
}
@Override
public Result findArticleById(Long articleId) {
/**
* 1. 根据id查询文章信息
* 2. 根据body id和categoryid去做关联查询
*/
Article article = articleMapper.selectById(articleId);
ArticleVo articleVo = copy(article, true, true,true,true);
//查看完文章,需要新增阅读数,需要执行更新操作,更新时会加写锁,阻塞其他读操作,性能低
//更新操作增加了此次接口的耗时,如果有问题就影响查看操作
//这是可以使用线程池,把更新操作放到线程池中执行和主线程就不相关了
String view_count = (String) redisTemplate.opsForHash().get("view_count", String.valueOf(articleId));
if(view_count != null){
articleVo.setViewCounts(Integer.parseInt(view_count));
}
threadService.updateArticleViewCount(articleMapper,article);
return Result.success(articleVo);
}
@Component
public class ThreadService {
//期望此操作在线程池中执行,不会影响原有的主线程
@Async("taskExecutor") //在线程池中执行此任务
public void updateArticleViewCount(ArticleMapper articleMapper, Article article) {
try {
int viewCounts = article.getViewCounts();
Article articleUpdate = new Article();
articleUpdate.setViewCounts(viewCounts+1);
LambdaUpdateWrapper<Article> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(Article::getId,article.getId());
//对id和viewCounts都进行判断,为了在多线程环境下,viewCounts不会被其他的线程已经修改过
updateWrapper.eq(Article::getViewCounts,viewCounts);
//update article set view_count = 100 where view_count=99 and id = 11
articleMapper.update(articleUpdate,updateWrapper);
Thread.sleep(5000);
System.out.println("更新完成了...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2. 定时任务
@Slf4j
@Component
// @EnableAsync
@EnableScheduling
public class HelloScheduled {
@Scheduled(cron = "* * * * * ?")
public void hello() {
log.info("hello...");
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
}
}
2023-09-24 17:29:36.001 INFO 9512 --- [ scheduling-1] c.l.g.seckill.scheduled.HelloScheduled : hello...
2023-09-24 17:29:40.014 INFO 9512 --- [ scheduling-1] c.l.g.seckill.scheduled.HelloScheduled : hello...
2023-09-24 17:29:44.005 INFO 9512 --- [ scheduling-1] c.l.g.seckill.scheduled.HelloScheduled : hello...
2023-09-24 17:29:48.015 INFO 9512 --- [ scheduling-1] c.l.g.seckill.scheduled.HelloScheduled : hello...
定时任务默认时阻塞的,可以通过定时任务和异步结合的方式来解决定时任务阻塞的问题
@Slf4j
@Component
@EnableAsync
@EnableScheduling
public class HelloScheduled {
/**
* 定时任务不该阻塞。默认是阻塞的
* 1)、可以让业务以异步的方式,自己提交到线程池
* CompletableFuture.runAsync(() -> {
* },execute);
*
* 2)、支持定时任务线程池;设置 TaskSchedulingProperties
* spring.task.scheduling.pool.size: 5
*
* 3)、让定时任务异步执行
* 异步任务
*
* 解决:使用异步任务 + 定时任务来完成定时任务不阻塞的功能
*
*/
@Async
@Scheduled(cron = "* * * * * ?")
public void hello() {
log.info("hello...");
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
}
}
2023-09-24 17:34:28.015 INFO 15092 --- [ task-3] c.l.g.seckill.scheduled.HelloScheduled : hello...
2023-09-24 17:34:29.003 INFO 15092 --- [ task-4] c.l.g.seckill.scheduled.HelloScheduled : hello...
2023-09-24 17:34:30.002 INFO 15092 --- [ task-5] c.l.g.seckill.scheduled.HelloScheduled : hello...