Java異步編程的5種異步實(shí)現(xiàn)方式詳解
1. 什么是異步?
首先我們先來(lái)看看一個(gè)同步的用戶注冊(cè)例子,流程如下:

在同步操作中,我們執(zhí)行到插入數(shù)據(jù)庫(kù)的時(shí)候,我們必須等待這個(gè)方法徹底執(zhí)行完才能執(zhí)行“發(fā)送短信”這個(gè)操作,如果插入數(shù)據(jù)庫(kù)這個(gè)動(dòng)作執(zhí)行時(shí)間較長(zhǎng),發(fā)送短信需要等待,這就是典型的同步場(chǎng)景。
于是聰明的人們開(kāi)始思考,如果兩者關(guān)聯(lián)性不強(qiáng),能不能將一些非核心業(yè)務(wù)從主流程中剝離出來(lái),于是有了異步編程雛形,改進(jìn)后的流程如下:

這就是異步編程,它是程序并發(fā)運(yùn)行的一種手段,它允許多個(gè)事件同時(shí)發(fā)生,當(dāng)程序調(diào)用需要長(zhǎng)時(shí)間運(yùn)行的方法時(shí),它不會(huì)阻塞當(dāng)前的執(zhí)行流程,程序可以繼續(xù)運(yùn)行。
在聊完異步編程后,那么我們一起來(lái)看看Java里面實(shí)現(xiàn)異步編程究竟有哪些方式呢?
2. 線程異步
在 Java 語(yǔ)言中最簡(jiǎn)單使用異步編程的方式就是創(chuàng)建一個(gè) 線程來(lái)實(shí)現(xiàn),如果你使用的 JDK 版本是 8 以上的話,可以使用 Lambda 表達(dá)式 會(huì)更加簡(jiǎn)潔。
public class AsyncThread extends Thread{
@Override
public void run() {
System.out.println("當(dāng)前線程名稱:" + this.getName() + ", 執(zhí)行線程名稱:" + Thread.currentThread().getName() + "-hello");
}
}
public static void main(String[] args) {
// 模擬業(yè)務(wù)流程
// .......
// 創(chuàng)建異步線程
AsyncThread asyncThread = new AsyncThread();
// 啟動(dòng)異步線程
asyncThread.start();
}當(dāng)然如果每次都創(chuàng)建一個(gè) Thread線程,頻繁的創(chuàng)建、銷毀,浪費(fèi)系統(tǒng)資源,我們可以采用線程池:
private ExecutorService executor = Executors.newCachedThreadPool() ;
public void fun() throws Exception {
executor.submit(new Runnable(){
@override
public void run() {
try {
// 要執(zhí)行的業(yè)務(wù)代碼,我們這里沒(méi)有寫方法,可以讓線程休息幾秒進(jìn)行測(cè)試
Thread.sleep(10000);
System.out.print("睡夠啦~");
}catch(Exception e) {
throw new RuntimeException("報(bào)錯(cuò)啦??!");
}
}
});
}將業(yè)務(wù)邏輯封裝到 Runnable 或 Callable 中,交由 線程池 來(lái)執(zhí)行
3. Future異步
上述方式雖然達(dá)到了多線程并行處理,但有些業(yè)務(wù)不僅僅要執(zhí)行過(guò)程,還要獲取執(zhí)行結(jié)果,后續(xù)提供在JUC包增加了Future。
從字面意思理解就是未來(lái)的意思,但使用起來(lái)卻著實(shí)有點(diǎn)雞肋,并不能實(shí)現(xiàn)真正意義上的異步,獲取結(jié)果時(shí)需要阻塞線程,或者不斷輪詢。
@Test
public void futureTest() throws Exception {
System.out.println("main函數(shù)開(kāi)始執(zhí)行");
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<Integer> future = executor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("===task start===");
Thread.sleep(5000);
System.out.println("===task finish===");
return 3;
}
});
// 這里需要返回值時(shí)會(huì)阻塞主線程,如果不需要返回值使用是OK的。倒也還能接受
// Integer result=future.get();
System.out.println("main函數(shù)執(zhí)行結(jié)束");
System.in.read();
}4. CompletableFuture異步
Future 類通過(guò) get() 方法阻塞等待獲取異步執(zhí)行的運(yùn)行結(jié)果,性能比較差。
JDK1.8 中,Java 提供了 CompletableFuture 類,它是基于異步函數(shù)式編程。相對(duì)阻塞式等待返回結(jié)果,CompletableFuture 可以通過(guò)回調(diào)的方式來(lái)處理計(jì)算結(jié)果,實(shí)現(xiàn)了異步非阻塞,性能更優(yōu)。
CompletableFuture 實(shí)現(xiàn)了 Future 和 CompletionStage 接口, 并提供了多種實(shí)現(xiàn)異步編程的方法,如supplyAsync, runAsync以及thenApplyAsync。
下面我們使用CompletableFuture來(lái)實(shí)現(xiàn)上面的例子:
CompletableFuture<Long> completableFuture = CompletableFuture.supplyAsync(() -> factorial(number));
while (!completableFuture.isDone()) {
System.out.println("CompletableFuture is not finished yet...");
}
long result = completableFuture.get();我們不需要顯式使用 ExecutorService,CompletableFuture 內(nèi)部使用了 ForkJoinPool 來(lái)處理異步任務(wù),這使得我們的代碼變的更簡(jiǎn)潔。
5. SpringBoot @Async異步
在@Async注解之前,使用多線程需要使用JDK的原生方法,非常麻煩,當(dāng)有了@Async之后就比較簡(jiǎn)單了。
首先,使用 @EnableAsync 啟用異步注解:
@SpringBootApplication
@EnableAsync
public class StartApplication {
public static void main(String[] args) {
SpringApplication.run(StartApplication.class, args);
}
}
// 自定義線程池:
@Configuration
@Slf4j
public class ThreadPoolConfiguration {
@Bean(name = "defaultThreadPoolExecutor", destroyMethod = "shutdown")
public ThreadPoolExecutor systemCheckPoolExecutorService() {
return new ThreadPoolExecutor(3, 10, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(10000),
new ThreadFactoryBuilder().setNameFormat("default-executor-%d").build(),
(r, executor) -> log.error("system pool is full! "));
}
}
// 在異步處理的方法上添加注解 @Async ,當(dāng)對(duì) execute 方法 調(diào)用時(shí),通過(guò)自定義的線程池 defaultThreadPoolExecutor 異步化執(zhí)行 execute 方法
@Service
public class AsyncServiceImpl implements AsyncService {
@Async("defaultThreadPoolExecutor")
public Boolean execute(Integer num) {
System.out.println("線程:" + Thread.currentThread().getName() + " , 任務(wù):" + num);
return true;
}
}用 @Async 注解標(biāo)記的方法,稱為異步方法。在spring boot應(yīng)用中使用 @Async 很簡(jiǎn)單:
- 調(diào)用異步方法類上或者啟動(dòng)類加上注解 @EnableAsync
- 在需要被異步調(diào)用的方法外加上 @Async
- 所使用的 @Async 注解方法的類對(duì)象應(yīng)該是Spring容器管理的bean對(duì)象;
6. Guava異步
Guava 提供了 ListenableFuture 類來(lái)執(zhí)行異步操作
1. 首先我們需要添加 guava 的maven依賴:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.2-jre</version>
</dependency>2. 現(xiàn)在我們使用ListenableFuture來(lái)實(shí)現(xiàn)我們之前的例子:
ExecutorService threadpool = Executors.newCachedThreadPool(); ListeningExecutorService service = MoreExecutors.listeningDecorator(threadpool); ListenableFuture<Long> guavaFuture = (ListenableFuture<Long>) service.submit(()-> factorial(number)); long result = guavaFuture.get();
這里使用MoreExecutors獲取ListeningExecutorService類的實(shí)例,然后ListeningExecutorService.submit執(zhí)行異步任務(wù),并返回 ListenableFuture實(shí)例。
到此這篇關(guān)于Java異步編程的5種異步實(shí)現(xiàn)方式詳解的文章就介紹到這了,更多相關(guān)Java異步編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot springmvc拋出全局異常的解決方法
這篇文章主要為大家詳細(xì)介紹了springboot springmvc拋出全局異常的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
Spring如何按業(yè)務(wù)模塊輸出日志到不同的文件詳解
最近做項(xiàng)目時(shí)有一個(gè)記錄操作日志的需求,比如某個(gè)用戶進(jìn)行了查詢、刪除、修改等操作,下面這篇文章主要給大家介紹了關(guān)于Spring如何按業(yè)務(wù)模塊輸出日志到不同文件的相關(guān)資料,需要的朋友可以參考下2022-05-05
Java中PriorityQueue實(shí)現(xiàn)最小堆和最大堆的用法
很多時(shí)候都會(huì)遇到PriorityQueue,本文主要介紹了Java中PriorityQueue實(shí)現(xiàn)最小堆和最大堆的用法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06
Java編程實(shí)現(xiàn)統(tǒng)計(jì)一個(gè)字符串中各個(gè)字符出現(xiàn)次數(shù)的方法
這篇文章主要介紹了Java編程實(shí)現(xiàn)統(tǒng)計(jì)一個(gè)字符串中各個(gè)字符出現(xiàn)次數(shù)的方法,涉及java針對(duì)字符串的遍歷、判斷、運(yùn)算等相關(guān)操作技巧,需要的朋友可以參考下2017-12-12
springboot static關(guān)鍵字真能提高Bean的優(yōu)先級(jí)(厲害了)
這篇文章主要介紹了springboot static關(guān)鍵字真能提高Bean的優(yōu)先級(jí)(厲害了),需要的朋友可以參考下2020-07-07
SpringBoot2.7?WebSecurityConfigurerAdapter類過(guò)期配置
這篇文章主要為大家介紹了SpringBoot2.7中WebSecurityConfigurerAdapter類過(guò)期應(yīng)該如何配置,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06

