Java 異步實現(xiàn)的幾種方式小結(jié)
Java 異步實現(xiàn)的幾種方式
1. jdk1.8之前的Future
jdk并發(fā)包里的Future代表了未來的某個結(jié)果,當(dāng)我們向線程池中提交任務(wù)的時候會返回該對象,可以通過future獲得執(zhí)行的結(jié)果,但是jdk1.8之前的Future有點雞肋,并不能實現(xiàn)真正的異步,需要阻塞的獲取結(jié)果,或者不斷的輪詢。
通常我們希望當(dāng)線程執(zhí)行完一些耗時的任務(wù)后,能夠自動的通知我們結(jié)果,很遺憾這在原生jdk1.8之前是不支持的,但是我們可以通過第三方的庫實現(xiàn)真正的異步回調(diào)。
/**
* jdk1.8之前的Future
* @author Administrator
*/
public class JavaFuture {
public static void main(String[] args) throws Throwable, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<String> f = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("task started!");
longTimeMethod();
System.out.println("task finished!");
return "hello";
}
});
//此處get()方法阻塞main線程
System.out.println(f.get());
System.out.println("main thread is blocked");
}
}
如果想獲得耗時操作的結(jié)果,可以通過get()方法獲取,但是該方法會阻塞當(dāng)前線程,我們可以在做完剩下的某些工作的時候調(diào)用get()方法試圖去獲取結(jié)果。
也可以調(diào)用非阻塞的方法isDone來確定操作是否完成,isDone這種方式有點兒類似下面的過程:

2. jdk1.8開始的Future
直到j(luò)dk1.8才算真正支持了異步操作,jdk1.8中提供了lambda表達(dá)式,使得java向函數(shù)式語言又靠近了一步。借助jdk原生的CompletableFuture可以實現(xiàn)異步的操作,同時結(jié)合lambada表達(dá)式大大簡化了代碼量。代碼例子如下:
package netty_promise;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
/**
* 基于jdk1.8實現(xiàn)任務(wù)異步處理
* @author Administrator
*/
public class JavaPromise {
public static void main(String[] args) throws Throwable, ExecutionException {
// 兩個線程的線程池
ExecutorService executor = Executors.newFixedThreadPool(2);
//jdk1.8之前的實現(xiàn)方式
CompletableFuture<String> future = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
System.out.println("task started!");
try {
//模擬耗時操作
longTimeMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
return "task finished!";
}
}, executor);
//采用lambada的實現(xiàn)方式
future.thenAccept(e -> System.out.println(e + " ok"));
System.out.println("main thread is running");
}
}
實現(xiàn)方式類似下圖:

3. Spring的異步方法
先把longTimeMethod 封裝到Spring的異步方法中,這個異步方法的返回值是Future的實例。這個方法一定要寫在Spring管理的類中,注意注解@Async。
@Service
public class AsynchronousService{
@Async
public Future springAsynchronousMethod(){
Integer result = longTimeMethod();
return new AsyncResult(result);
}
}
其他類調(diào)用這個方法。這里注意,一定要其他的類,如果在同類中調(diào)用,是不生效的。
@Autowired
private AsynchronousService asynchronousService;
public void useAsynchronousMethod(){
Future future = asynchronousService.springAsynchronousMethod();
future.get(1000, TimeUnit.MILLISECONDS);
}
其實Spring只不過在原生的Future中進(jìn)行了一次封裝,我們最終獲得的還是Future實例。
4. Java如何將異步調(diào)用轉(zhuǎn)為同步
換句話說,就是需要在異步調(diào)用過程中,持續(xù)阻塞至獲得調(diào)用結(jié)果。
- 使用wait和notify方法
- 使用條件鎖
- Future
- 使用CountDownLatch
- 使用CyclicBarrier
五種方法,具體舉例說明看這篇文章
java異步任務(wù)處理
1、場景
最近做項目的時候遇到了一個小問題:從前臺提交到服務(wù)端A,A調(diào)用服務(wù)端B處理超時,原因是前端一次請求往db插1萬數(shù)據(jù),插完之后會去清理緩存、發(fā)送消息。
服務(wù)端的有三個操作 a、插DB b、清理cache c、發(fā)送消息。1萬條數(shù)據(jù),說多不多,說少不少.況且不是單單insert。出現(xiàn)超時現(xiàn)象,不足為奇了吧~~
2、分析
如何避免超時呢?一次請求處理辣么多數(shù)據(jù),可分多次請求處理,每次請求處理100條數(shù)據(jù),可以有效解決超時問題. 為了不影響用戶的體驗,請求改為ajax 異步請求。
除此之外,仔細(xì)分析下場景. a 操作是本次處理的核心. 而b、c操作可以異步處理。換句話說,a操作處理完可以馬上返回給結(jié)果, 不必等b、c處理完再返回。b、c操作可以放在一個異步任務(wù)去處理。
3、實戰(zhàn)
(1)、ExecutorService : 任務(wù)提交
(2)、demo
異步任務(wù)類
public class ExecutorDemo {
private ExecutorService executor = Executors.newFixedThreadPool(1);
public void asynTask() throws InterruptedException {
executor.submit(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10000);//方便觀察結(jié)果
} catch (InterruptedException e) {
e.printStackTrace();
}
int sum = 0;
for(int i = 0; i < 1000; i++) {
sum += i;
}
System.out.println(sum);
}
});
}
}
客戶端模擬
public class Client {
public static void main(String[] args) throws InterruptedException {
boolean r = task2();
if(r) {
task3();
}
System.out.println("------------main end-----------");
}
static boolean task2() throws InterruptedException {
ExecutorDemo e = new ExecutorDemo();
e.asynTask();
System.out.println("------------task2 end-----------");
return true;
}
static void task3() throws InterruptedException {
int j = 0;
while(true) {
if(j++ > 10000) {
break;
}
}
System.out.println("------------task3 end-----------");
}
}
結(jié)果是醬紫的
------------task2 end-----------
------------task3 end-----------
------------main end-----------
499500
我們來分析下結(jié)果, task2是個異步任務(wù),執(zhí)行到task2,主線程不會在task2 阻塞,不用等待task2 處理完,可以往下執(zhí)行task3.
相關(guān)文章
Spring MVC請求參數(shù)接收的全面總結(jié)教程
這篇文章主要給大家總結(jié)介紹了關(guān)于Spring MVC請求參數(shù)接收的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08
Java?MyBatis是如何執(zhí)行一條SQL語句的
這篇文章主要介紹了Java?MyBatis是如何執(zhí)行一條SQL語句的,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-07-07
東方通TongWeb結(jié)合Spring-Boot使用的實現(xiàn)
本文主要介紹了東方通TongWeb結(jié)合Spring-Boot使用的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07
BaseDao封裝JavaWeb的增刪改查的實現(xiàn)代碼
Basedao 是一種基于數(shù)據(jù)訪問對象(Data Access Object)模式的設(shè)計方法,它是一個用于處理數(shù)據(jù)庫操作的基礎(chǔ)類,負(fù)責(zé)封裝數(shù)據(jù)庫訪問的底層操作,提供通用的數(shù)據(jù)庫訪問方法,本文給大家介紹了BaseDao封裝JavaWeb的增刪改查的實現(xiàn)代碼,需要的朋友可以參考下2024-03-03
SpringBoot和前端聯(lián)動實現(xiàn)存儲瀏覽記錄功能
這篇文章主要介紹了SpringBoot和前端聯(lián)動實現(xiàn)存儲瀏覽記錄功能,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-01-01
Java如何使用正則表達(dá)式從字符串中提取數(shù)字
這篇文章主要介紹了Java如何使用正則表達(dá)式從字符串中提取數(shù)字問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
解決cmd執(zhí)行javac報錯:不是內(nèi)部或外部命令,也不是可運行的程序
剛接觸JAVA的新手可能就不知道怎么解決'JAVAC'不是內(nèi)部命令或外部命令,這篇文章主要給大家介紹了關(guān)于解決cmd執(zhí)行javac報錯:不是內(nèi)部或外部命令,也不是可運行的程序的相關(guān)資料,需要的朋友可以參考下2023-11-11

