Future和AsyncResult是什么吏饿?后面再看!
先直接上代碼:
Demo 1
- 第一步脆霎,用最簡單的方法生成一個可運行的Spring Boot項目:https://start.spring.io/ (Dependencies(依賴)加個Spring Web)
- 生成的項目中,找到啟動類DemoApplication,添加異步注解@EnableAsync
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
- 新建 FutureService.java丁眼,里面有三個方法,返回值用 AsyncResult 包裝一下昭殉,為了方便看效果苞七,加上一些日志
package com.example.demo.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import java.util.concurrent.Future;
@Service
@Slf4j
public class FutureService {
@Async
public Future<String> jobOne() throws InterruptedException {
System.out.println("job 1 start...");
long start = System.currentTimeMillis();
Thread.sleep(2000);
long end = System.currentTimeMillis();
System.out.println("job 1 completed, it took " + (end - start));
return new AsyncResult<>( "job 1 success");
}
@Async
public Future<String> jobTwo() throws InterruptedException {
System.out.println("job 2 start...");
long start = System.currentTimeMillis();
Thread.sleep(500);
long end = System.currentTimeMillis();
System.out.println("job 2 completed, it took " + (end - start));
return new AsyncResult<>( "job 2 success");
}
@Async
public Future<String> jobThree() throws InterruptedException {
System.out.println("job 3 start...");
long start = System.currentTimeMillis();
Thread.sleep(1000);
long end = System.currentTimeMillis();
System.out.println("job 3 completed, it took "+ (end - start));
return new AsyncResult<>( "job 3 success");
}
}
- 新建 DemoControllerl.java,調(diào)用 FutureService 里的三個方法
package com.example.demo.controller;
import com.example.demo.service.FutureService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@RestController
public class DemoController {
@Autowired
private FutureService futureService;
@RequestMapping("/test")
public String test() throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
Future<String> f1 = futureService.jobOne();
Future<String> f2 = futureService.jobTwo();
Future<String> f3 = futureService.jobThree();
// 三個方法都執(zhí)行完畢才繼續(xù)執(zhí)行后面代碼
while (!f1.isDone() || !f2.isDone() || !f3.isDone()){
System.out.println("Waiting....");
Thread.sleep(300);
}
// 三個方法都執(zhí)行完畢
long end = System.currentTimeMillis();
System.out.println("Result 1: " + f1.get());
System.out.println("Result 2: " + f2.get());
System.out.println("Result 3: " + f3.get());
System.out.println("Execution Time : " + (end - start));
return "success";
}
}
- 運行啟動類 DemoApplication挪丢,啟動成功后訪問 http://localhost:8080/test蹂风, 可以看到以下效果:
job 1 start...
job 2 start...
Waiting....
job 3 start...
Waiting....
job 2 completed, it took 501
Waiting....
Waiting....
job 3 completed, it took 1000
Waiting....
Waiting....
Waiting....
job 1 completed, it took 2002
Result 1: job 1 success
Result 2: job 2 success
Result 3: job 3 success
Execution Time : 2114
從打印的日志可以看出來,并不是按照三個方法調(diào)用的順序乾蓬,一個方法執(zhí)行完再執(zhí)行下一個方法惠啄,三個方法的運行時間:501任内、1000撵渡、2002,而總的運行時間為2114死嗦,這個例子中趋距,三個方法是異步執(zhí)行的。越除。
Demo 2
還是用上面的代碼棚品,但是 DemoController 是代碼順序稍微調(diào)整一下:按照平時的習慣,執(zhí)行一個方法廊敌,輸出結(jié)果铜跑。
@RestController
public class DemoController {
@Autowired
private FutureService futureService;
@RequestMapping("/test")
public String test() throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
Future<String> f1 = futureService.jobOne();
System.out.println("Result 1: " + f1.get());
Future<String> f2 = futureService.jobTwo();
System.out.println("Result 2: " + f2.get());
Future<String> f3 = futureService.jobThree();
System.out.println("Result 3: " + f3.get());
long end = System.currentTimeMillis();
System.out.println("Execution Time : " + (end - start));
return "success";
}
}
重新運行啟動類 DemoApplication,可以看到以下結(jié)果:
job 1 start...
job 1 completed, it took 2005
Result 1: job 1 success
job 2 start...
job 2 completed, it took 502
Result 2: job 2 success
job 3 start...
job 3 completed, it took 1004
Result 3: job 3 success
Execution Time : 3516
這次的輸出結(jié)果骡澈,和沒有用Future和AsyncResult的方法是一樣的效果锅纺,每個方法按順序執(zhí)行,一個結(jié)果后再執(zhí)行下一個肋殴。
概念
Future
Future是 java.util.concurrent 包里的一個接口
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html
- cancel(boolean mayInterruptIfRunning): boolean
- get()
- get(long timeout, TimeUnit unit)
- isCancelled(): boolean
- isDone(): boolean
2022.9.14 更新
最近由于項目上對一些慢接口的優(yōu)化囤锉,把很多接口加上了@Async坦弟,上線運行一段時間后,發(fā)現(xiàn)線程數(shù)量激增9俚亍D鸢!
@Async默認使用Spring的SimpleAsyncTaskExecutor:
TaskExecutor implementation that fires up a new Thread for each task, executing it asynchronously.
每提交一個任務驱入,就會創(chuàng)建一個線程赤炒,能不炸嗎?
解決方法: @Async(value="customExecutor")亏较, value參數(shù)傳入自定義的線程池莺褒。