前言:
项目运行时出现的异常,我们需要将异常信息记录到日志,有时还需要插入数据库。
例如:项目中的用户登录模块,当用户登录系统,我们除了需要校验用户名、密码、验证码等操作外,还需要将用户登录的信息记录到日志,并且往数据库中插入一条记录。
如果采用同步操作,执行效率势必很低。建议采用异步操作。
本章节主要将 在 SpringBoot项目中 如何使用异步操作。
一、线程池配置
为什么采用线程池?
线程池 与 数据库连接池类似。 引入池的概念,主要解决效率和资源的利用率问题。当频繁创建、销毁线程会导致 效率低下,而且大量浪费内存。
线程池配置:往容器中注入异步调用线程池对象 scheduledExecutorService
package com.ruoyi.common.config.thread;
import com.ruoyi.common.utils.Threads;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池配置
*
* @author ruoyi
**/
@Configuration
public class ThreadPoolConfig {
// 核心线程池大小
private int corePoolSize = 50;
/**
* 执行周期性或定时任务
*/
@Bean(name = "scheduledExecutorService")
protected ScheduledExecutorService scheduledExecutorService() {
return new ScheduledThreadPoolExecutor(corePoolSize,
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
Threads.printException(r, t);
}
};
}
}
二、异步任务管理器
package com.ruoyi.framework.manager;
import com.ruoyi.common.utils.spring.SpringUtils;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 异步任务管理器
*
* @author liuhulu
*/
public class AsyncManager {
/**
* 操作延迟10毫秒
*/
private final int OPERATE_DELAY_TIME = 10;
/**
* 异步操作任务调度线程池
*/
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
/**
* 单例模式
*/
private AsyncManager() {
}
private static AsyncManager me = new AsyncManager();
public static AsyncManager me() {
return me;
}
/**
* 执行任务
*
* @param task 任务
*/
public void execute(TimerTask task) {
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
}
}
三、 测试异步调用
此处仅用于 做异步调用的测试,本来准备写测试类的,但后来发现,只有在main这个线程上,才能启动其他线程,所以如果写测试类的方法是没法 完成异步调用。 故此把测试直接写到了 application 的主类中,测试完成后删除测试部分即可
package com.ruoyi;
import com.ruoyi.framework.manager.AsyncManager;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.TimerTask;
@SpringBootApplication
@MapperScan("com.ruoyi.**.mapper")
public class RuoyiApplication {
public static void main(String[] args) {
SpringApplication.run(RuoyiApplication.class, args);
ayscTest();
}
public static void ayscTest() {
long start = System.currentTimeMillis();
System.out.println("AsyncManagerTest.execute()..." + Thread.currentThread().getName());
AsyncManager.me().execute(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "start");
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
AsyncManager.me().execute(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "start");
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
long end = System.currentTimeMillis();
System.out.println("总共耗时: " + (end - start));
}
}