@Async 註解的用法和示例
背景
通常,在Java中的方法呼叫都是同步呼叫,比如在A方法中呼叫了B方法,則在A呼叫B方法之後,必須等待B方法執行並返回後,A方法才可以繼續往下執行。這樣容易出現的一個問題就是如果B方法執行時間較長,則可能會導致呼叫A的請求響應遲緩,為了解決這種問題,可以使用Spirng的註解@Async來用非同步呼叫的方式處理,當然也會有別的多執行緒方式解決此類問題,本文主要分析@Async在解決此類問題時的用法以及具體的示例。
非同步呼叫
比如方法A呼叫方法B,如果B是一個非同步方法,則A方法在呼叫B方法之後,不用等待B方法執行完成,而是直接往下繼續執行別的程式碼。
@Async介紹
在Spring中,使用@Async標註某方法,可以使該方法變成非同步方法,這些方法在被呼叫的時候,將會在獨立的執行緒中進行執行,呼叫者不需等待該方法執行完成。
在Spring中啟用@Async
使用@EnableAsync
@Slf4j
@SpringBootApplication
@ComponentScan(basePackages = {"com.kaesar.spring"})
@EnableAsync // 開啟非同步呼叫
public class Application {
public static void main(String[] args) {
log.info("spring boot開始啟動...");
ApplicationContext ctx = SpringApplication.run(Application.class, args);
String[] activeProfiles = ctx.getEnvironment().getActiveProfiles();
for (String profile : activeProfiles) {
log.info("當前環境為:" + profile);
}
log.info("spring boot啟動成功...");
}
}
示例一:基本使用方式
在方法上新增@Async註解
/**
* 非同步方法
* 預設情況下,Spring 使用 SimpleAsyncTaskExecutor 去執行這些非同步方法(此執行器沒有限制執行緒數)。
* 此預設值可以從兩個層級進行覆蓋:
* 方法級別
* 應用級別
*/
@Async
public void test2() {
try {
log.info(Thread.currentThread().getName() + " in test2, before sleep.");
Thread.sleep(2000);
log.info(Thread.currentThread().getName() + " in test2, after sleep.");
} catch (InterruptedException e) {
log.error("sleep error.");
}
}
呼叫非同步方法
/**
* 呼叫不同類的非同步方法
*/
public void func1() {
log.info("before call async function.");
asyncService.test2();
log.info("after call async function.");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
log.error("sleep error.");
}
log.info("func end.");
}
執行結果
從執行結果可以看出,main執行緒中的func1方法在呼叫非同步方法test2後,沒有等待test2方法執行完成,直接執行後面的程式碼。
示例二:在同一個類中呼叫非同步方法
方法func2和上面的非同步方法test2方法在同一個類中
從執行結果可知,main執行緒中的func2方法在呼叫非同步方法test2方法後,等待test2方法執行完後,才繼續往後執行。
示例三:非同步方法是static方法
非同步方法test3是一個static方法
/**
* 非同步方法不能是 static 方法,不然註解失效
*/
@Async
public static void test3() {
try {
log.info(Thread.currentThread().getName() + " in test3, before sleep.");
Thread.sleep(2000);
log.info(Thread.currentThread().getName() + " in test3, after sleep.");
} catch (InterruptedException e) {
log.error("sleep error.");
}
}
呼叫test3的方法
/**
* 呼叫不同類的非同步方法,非同步方法是 static 方法
*/
public void func3() {
log.info(Thread.currentThread().getName() + ": before call async function.");
AsyncService.test3();
log.info(Thread.currentThread().getName() + ": after call async function.");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
log.error("sleep error.");
}
log.info(Thread.currentThread().getName() + ": func end.");
}
執行結果。可以看出在static方法上新增@Async註解,當呼叫該方法時並沒有新啟用一個執行緒單獨執行,而是按順序執行程式碼,說明非同步無效。
示例四:在方法級別上修改預設的執行器
自定義一個執行緒池執行器代替預設的執行器
自定義的執行緒池執行器
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
* 自定義執行緒池
*/
@Configuration
public class AsyncConfig {
private static final int MAX_POOL_SIZE = 10;
private static final int CORE_POOL_SIZE = 5;
@Bean("asyncTaskExecutor")
public AsyncTaskExecutor asyncTaskExecutor() {
ThreadPoolTaskExecutor asyncTaskExecutor = new ThreadPoolTaskExecutor();
asyncTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
asyncTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
asyncTaskExecutor.setThreadNamePrefix("async-task-thread-pool-");
asyncTaskExecutor.initialize();
return asyncTaskExecutor;
}
}
非同步方法上使用自定義的執行器
/**
* 在方法級別上修改預設的執行器
*/
@Async("asyncTaskExecutor")
public void test4() {
try {
log.info(Thread.currentThread().getName() + ": in test4, before sleep.");
Thread.sleep(2000);
log.info(Thread.currentThread().getName() + ": in test4, after sleep.");
} catch (InterruptedException e) {
log.error("sleep error.");
}
}
呼叫test4非同步方法
/**
* 呼叫不同類的非同步方法
*/
public void func4() {
log.info(Thread.currentThread().getName() + ": before call async function.");
asyncService.test4();
log.info(Thread.currentThread().getName() + ": after call async function.");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
log.error("sleep error.");
}
log.info(Thread.currentThread().getName() + ": func end.");
}
從執行結果可以看出,@Async註解宣告使用指定的自定義的非同步執行器,已經替換了預設的執行器。而且呼叫非同步方法的main執行緒沒有等待非同步方法的執行。
說明:新建自定義的執行器後,註解@Async預設就會替換成自定義的執行器,所以在@Async註解上可以不用指定。
\(1.01^{365} ≈ 37.7834343329\)
\(0.99^{365} ≈ 0.02551796445\)
相信堅持的力量!