1.主启动类添加@EnableAsync注解
@EnableAsync
@SpringBootApplication
public class MySpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringbootApplication.class, args);
System.out.println("=======启动成功========");
}
}
2.自定义线程池
@Async默认异步配置使用的是SimpleAsyncTaskExecutor,该线程池默认来一个任务创建一个线程,若系统中不断的创建线程,最终会导致系统占用内存过高,引发OutOfMemoryError错误,所以最好自定义一个线程池。
/**
* 线程池参数配置,多个线程池实现线程池隔离,@Async注解,默认使用系统自定义线程池
**/
@EnableAsync
@Configuration
public class TaskPoolConfig {
@Bean("my-executor")
public Executor taskExecutor() {
//返回可用处理器的Java虚拟机的数量
int i = Runtime.getRuntime().availableProcessors();
System.out.println("系统最大线程数:" + i);
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程池大小
executor.setCorePoolSize(i);
//最大线程数
executor.setMaxPoolSize(i * 5);
//配置队列容量,默认值为Integer.MAX_VALUE
executor.setQueueCapacity(99999);
//活跃时间
executor.setKeepAliveSeconds(60);
//线程名字前缀
executor.setThreadNamePrefix("my-thread-executor-");
//设置此执行程序应该在关闭时阻止的最大秒数,以便在容器的其余部分继续关闭之前等待剩余的任务完成他们的执行
executor.setAwaitTerminationSeconds(60);
//等待所有的任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
3.异步调用:无返回值和有返回值
代码示例:
@RestController
@RequestMapping("/async")
public class AsyncController {
@Autowired
private AsyncService asyncService;
/**
* 无返回值
*
* @return
*/
@RequestMapping("/testOne")
public String testOne() {
try {
System.out.println("one主线程开始执行任务: " + Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
//无返回值
asyncService.doAsyncOne();
System.out.println("one主线程执行任务完成: " + Thread.currentThread().getName());
return "success";
}
/**
* 有返回值
*
* @return
* @throws ExecutionException
* @throws InterruptedException
*/
@RequestMapping("/testTwo")
public String testTwo() throws ExecutionException, InterruptedException {
try {
System.out.println("two主线程开始执行任务: " + Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
//有返回值
Future<String> future = asyncService.doAsyncTwo();
//get()方法会阻塞主线程
System.out.println("异步线程结果: " + future.get());
System.out.println("two主线程执行任务完成: " + Thread.currentThread().getName());
return "success";
}
}
@Service
public class AsyncService {
@Async("my-executor")
public void doAsyncOne() {
System.out.println("异步线程one开始执行任务: " + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(3);
System.out.println("异步线程one执行任务结束: " + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
@Async("my-executor")
public Future<String> doAsyncTwo() {
System.out.println("异步线程two开始执行任务: " + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(3);
System.out.println("异步线程two执行任务结束: " + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
return new AsyncResult<>("async success");
}
}
测试结果:
(1).请求路径:localhost:8088/async/testOne
(2).请求路径:localhost:8088/async/testTwo
4.@Async和@Transactional注解配合使用
(1).失效情况:调用的主方法加@Transactional事务注解。
@RestController
@RequestMapping("/async")
public class AsyncController {
@Autowired
private AsyncService asyncService;
/**
* 事务测试
*/
@Transactional
@RequestMapping("/testTransaction")
public void testTransaction() {
try {
System.out.println("主线程开始执行任务: " + Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
asyncService.doTransaction();
}
}
@Service
public class AsyncService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Async
public void doTransaction() {
System.out.println("异步线程开始执行任务: " + Thread.currentThread().getName());
String sql = "insert into students values (null,'durant','chinese', 88)";
jdbcTemplate.execute(sql);
throw new RuntimeException("exception occured");
}
}
由于调用的主方法和异步方法不是同一个线程,事务注解无法生效,抛异常之后事务没有回滚入,库成功。
(2).正常情况:@Transactional事务注解加在异步方法上。
@RestController
@RequestMapping("/async")
public class AsyncController {
@Autowired
private AsyncService asyncService;
/**
* 事务测试
*/
@RequestMapping("/testTransaction")
public void testTransaction() {
try {
System.out.println("主线程开始执行任务: " + Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
asyncService.doTransaction();
}
}
@Service
public class AsyncService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Async
@Transactional
public void doTransaction() {
System.out.println("异步线程开始执行任务: " + Thread.currentThread().getName());
String sql = "insert into students values (null,'durant','chinese', 88)";
jdbcTemplate.execute(sql);
throw new RuntimeException("exception occured");
}
}
异步方法抛出异常,事务回滚,没有入库。
5.@Async注解使用注意事项:
1.异步方法必须是public类型,返回值只能是void类型或者Future类型。
2.异步类必须交给Spring容器管理,添加@Component注解(或其他注解)。
3.调用方法不能和异步调用方法在通一个类中(this调用)。
4.异步类需要使用@Autowired或@Resource等注解自动注入,不能手动new对象。