使用場景
Spring Boot中@Async和Future的使用場景適合於以下場景:
- 當前運行的任務可以分為N步分解時,例如一個統計需要統計三項數據,分別來源於三個表,那么我們可以把統計分為三個接口,在控制層使用Future調用任務。這種情況在控制層還是處於阻塞狀態。
- 當前運行的任務不關心另外一個任務的運行結果,我們可以直接使用@Async實現異步調用。
- 其它需要異步調用的方法。
下面我們使用一個Spring Boot工程說明這1、2兩種情況:
代碼詳解
- pom.xml引入spring-boot-starter-web。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.新建一個接口:PiceaService,里面包含三個方法
public interface PiceaService {
//無返回參數方法
void asyncTask() throws Exception;
//有返回參數方法
Future<String> asyncTaskFuture() throws Exception;
//有返回參數方法2
Future<String> asyncTaskFuture2() throws Exception;
}
3.新建接口實現類 PiceaServiceImpl。
要點:
1)注意每個方法上面的注解@Async,這個一定要寫,否則就不是異步任務
@Service
public class PiceaServiceImpl implements PiceaService {
@Async
@Override
public void asyncTask() throws Exception {
System.out.println("無返回異步線程,線程名:" + Thread.currentThread().getName());
System.out.println("無返回異步處理方法-----start-------");
Thread.sleep(5000);
System.out.println("無返回異步處理方法------end--------");
}
@Async
@Override
public Future<String> asyncTaskFuture() throws Exception {
System.out.println("有返回睡眠異步線程,線程名:" + Thread.currentThread().getName());
System.out.println("有返回睡眠異步處理方法-----start-------1---");
int k = 1;
Thread.sleep(5000);
System.out.println("有返回睡眠異步處理方法-----end----------1--");
return new AsyncResult<String>(String.valueOf(k));
}
@Async
@Override
public Future<String> asyncTaskFuture2() throws Exception {
System.out.println("有返回異步線程,線程名:" + Thread.currentThread().getName());
System.out.println("有返回異步處理方法-----start------------2---");
int k = 2;
System.out.println("有返回異步處理方法-----end--------------2---");
return new AsyncResult<String> (String.valueOf(k));
}
}
4.然后我們建立一個控制類 PiceaContoller。
@RestController
public class PiceaContoller {
@Autowired
private PiceaService piceaService;
@RequestMapping("/asyncTask")
public void asyncTask() throws Exception {
piceaService.asyncTask();
}
@RequestMapping("/asyncTaskFuture")
public String asyncTaskFuture() throws Exception {
String ret = null;
//異步先執行任務1
Future<String> future = piceaService.asyncTaskFuture();
//異步執行任務2
Future<String> future2 = piceaService.asyncTaskFuture2();
String ret1 = null;
String ret2 = null;
//獲取任務1執行結果
while (true) {
if (future.isDone()){
ret1 = future.get();
break;
}
}
//獲取任務2執行結果
while (true) {
if (future2.isDone()) {
ret2 = future2.get();
break;
}
}
//任務1結果+任務2結果
ret = ret1 + "+" + ret2;
//最終返回任何合集
return ret;
}
}
5.最后一步,不要忘記在啟動類加異步服務開啟的注解@EnableAsync。
@EnableAsync
@SpringBootApplication
public class SpringBootAsync1Application {
public static void main(String[] args) {
SpringApplication.run(SpringBootAsync1Application.class, args);
}
}
大功告成!趕緊回家找媽媽!
-
查看結果
從結果上,可以明顯看出兩個異步執行的任務,因為我在代碼中,讓任務1休眠了1秒。

總結:
通過異步的方式可以間接提高程序的運行能力,異步適用於邏輯沒有關聯的工作,比如原題說的統計。
其次也可以創建異步線程池來進行異步控制
線程池使用如下:
- 新建一個線程配置類:ThreadPoolTaskConfig
要點:
1)增加@Configuration注解,確定是配置類。
2)增加@EnableAsync,把異步服務開啟轉移到線程配置類。
@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {
private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() *2;
private static final int MAX_POOL_SIZE = CORE_POOL_SIZE *4 <256 ? 256 : CORE_POOL_SIZE * 4;
private static final int KEEP_ALIVE_TIME = 10; //允許線程空閑時間(單位為秒)
private static final int QUEUE_CAPACITY = 200; // 緩沖隊列數
private static final int AWAIT_TERMINATION = 60;//線程池中任務的等待時間,如果超過這個時候還沒有銷毀就強制銷毀
private static final Boolean WAIT_FOR_TASKS_TO_COMPLETE_ON_SHUTDOWN = true;//用來設置線程池關閉的時候等待所有任務都完成再繼續銷毀其他的Bean
private static final String THREAD_NAME_PREFIX = "PiceaAsync-Service-"; // 線程池名前綴
/**
* <p>"@Bean("piceaTaskExecutor"),Bean后面的()內容可以省略
* 如果省略則使用方法名<p>
* @author jiangbing.yang
* @date 2019/3/27 11:07
* @params
* @return
* @throws
*/
@Bean("piceaTaskExecutor")
public ThreadPoolTaskExecutor piceaTaskExecutor () {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
taskExecutor.setKeepAliveSeconds(KEEP_ALIVE_TIME);
taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
taskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX);
taskExecutor.setWaitForTasksToCompleteOnShutdown(WAIT_FOR_TASKS_TO_COMPLETE_ON_SHUTDOWN);
taskExecutor.setAwaitTerminationSeconds(AWAIT_TERMINATION);
// 線程池對拒絕任務的處理策略
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 初始化
taskExecutor.initialize();
return taskExecutor;
}
}
制定異步方法使用線程池執行
@Service
public class PiceaServiceImpl implements PiceaService {
@Async("piceaTaskExecutor")
@Override
public void asyncTask() throws Exception {
System.out.println("異步線程,線程名:" + Thread.currentThread().getName());
System.out.println("異步處理方法-----start-------");
System.out.println("------------------------在看貂蟬,不要打擾--------------");
Thread.sleep(1000);
System.out.println("異步處理方法------end--------");
}
@Async("piceaTaskExecutor")
@Override
public Future<String> asyncTaskFuture() throws Exception {
System.out.println("異步線程,線程名:" + Thread.currentThread().getName());
System.out.println("異步處理方法-----start-------asyncTaskFuture---");
int k = 1;
Thread.sleep(1000);
System.out.println("異步處理方法-----end---------asyncTaskFuture---");
return new AsyncResult<String> (String.valueOf(k));
}
@Async("piceaTaskExecutor")
@Override
public Future<String> asyncTaskFuture2() throws Exception {
System.out.println("異步線程,線程名:" + Thread.currentThread().getName());
System.out.println("異步處理方法-----start-------asyncTaskFuture-----2---");
int k = 2;
System.out.println("異步處理方法-----end---------asyncTaskFuture-----2---");
return new AsyncResult<String> (String.valueOf(k));
}
}
