新聞中心
什么是異步編程
在很多時候,我們在進(jìn)程中使用單一線程從頭到尾地執(zhí)行程序,比如程序向另外一臺服務(wù)器發(fā)出請求,由于網(wǎng)絡(luò)等外部原因,此種通信任務(wù)往往會耗費(fèi)大量時間,進(jìn)程如果在此期間僅僅只能等待網(wǎng)絡(luò)或網(wǎng)絡(luò)上其他機(jī)器的響應(yīng),將嚴(yán)重地降低了性能。

創(chuàng)新互聯(lián)服務(wù)緊隨時代發(fā)展步伐,進(jìn)行技術(shù)革新和技術(shù)進(jìn)步,經(jīng)過10年的發(fā)展和積累,已經(jīng)匯集了一批資深網(wǎng)站策劃師、設(shè)計師、專業(yè)的網(wǎng)站實(shí)施團(tuán)隊以及高素質(zhì)售后服務(wù)人員,并且完全形成了一套成熟的業(yè)務(wù)流程,能夠完全依照客戶要求對網(wǎng)站進(jìn)行網(wǎng)站設(shè)計、成都網(wǎng)站建設(shè)、建設(shè)、維護(hù)、更新和改版,實(shí)現(xiàn)客戶網(wǎng)站對外宣傳展示的首要目的,并為客戶企業(yè)品牌互聯(lián)網(wǎng)化提供全面的解決方案。
如果程序調(diào)用某個方法,等待其執(zhí)行全部處理后才能繼續(xù)執(zhí)行,我們稱其為同步的。相反,在處理完成之前就返回調(diào)用方法則是異步的。
我們在編程語言的流程中添加了異步控制的部分,這部分的編程可以稱之為異步編程。
JDK中的異步編程
Future
Future模式在 JDK5 的時候就有, Future模式,只是發(fā)起了耗時操作,函數(shù)立馬就返回了,真正執(zhí)行具體操作由另外一個工作線程去完成,并不會阻塞客戶端線程。所以在工作線程執(zhí)行耗時操作的時候客戶端無需等待,可以繼續(xù)做其他事情,等到需要的時候再向工作線程獲取結(jié)果。
舉個最簡單的例子,我們燒水的時候么,不用一直在爐子旁邊看著,在燒水的過程中,我們需要做一些其他的事情,比如去寫一會代碼,但是在你去寫代碼之前,會給你一個假的結(jié)果,比如,我已經(jīng)燒開了,但是,在你去寫代碼的時候,他就開始瘋狂加火,等到水燒開為止,等到你口渴想倒水的時候,發(fā)現(xiàn)水是已經(jīng)燒開的,也就是說,當(dāng)你在寫代碼之前的時候收到的是個假的結(jié)果。
實(shí)際上,F(xiàn)uture 模式無法立即給出你想要的結(jié)果,但它會給你一個契約,之后你可以隨時通過這個契約來獲取你想要的結(jié)果。
異步模式主要是和同步模式進(jìn)行對比的,我們畫個圖來看看。
黃色區(qū)域的位置的Y軸長度則表示的是你需要等待的所有時間,在這個時間內(nèi),你沒有辦法做任何的事情,只能在這里等著,但是異步的話,就完全不用這個樣子了。
在JDK中Future模式有一套完整的實(shí)現(xiàn)。
我們來寫案例代碼實(shí)驗(yàn)一下:
沒有是用 Future 的代碼。
NormalThreadTest
public class NormalThreadTest {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
// 開啟購買廚具線程
ShoppingThread shopping = new ShoppingThread();
shopping.start();
shopping.join(); // 保障廚具購買并送貨
// 獲取到購買廚具
KitchenWare kc = shopping.kc;
// 買食材
FoodMaterial fm = new FoodMaterial();
Thread.sleep(2000);
System.out.println("第二步: 食材已經(jīng)到位");
// 烹飪美食
cooking(kc, fm);
System.out.println("第三步: 美食烹飪完成");
long end = System.currentTimeMillis();
System.out.println("烹飪美食時間為:" + (end - start));
}
/**
* 定義網(wǎng)上購物廚具線程
* @author Administrator
*
*/
static class ShoppingThread extends Thread {
// 廚具對象引用
private KitchenWare kc;
@Override
public void run() {
System.out.println("第一步: 網(wǎng)上下單");
System.out.println("第一步: 等待廚具");
try {
Thread.sleep(5000); // 等待廚具時間
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第一步: 快遞送貨");
// 生產(chǎn)廚具
kc = new KitchenWare();
}
}
/**
* 廚具類
* @author Administrator
*
*/
static class KitchenWare {
}
/**
* 食材類
* @author Administrator
*
*/
static class FoodMaterial {
}
/**
* 定義烹飪食物的方法
* @param kc
* @param fm
*/
static void cooking(KitchenWare kc, FoodMaterial fm) {
}
}
運(yùn)行結(jié)果:
第一步: 網(wǎng)上下單
第一步: 等待廚具
第一步: 快遞送貨
第二步: 食材已經(jīng)到位
第三步: 美食烹飪完成
烹飪美食時間為:7043
已經(jīng)使用Future的代碼。
FutureThreadTest
public class FutureThreadTest {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
Callable callable = new Callable() {
public KitchenWare call() throws Exception {
System.out.println("第一步: 網(wǎng)上下單");
System.out.println("第一步: 等待廚具");
try {
Thread.sleep(5000); // 等待廚具時間
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第一步: 快遞送貨");
return new KitchenWare();
}
};
// 包裝為異步執(zhí)行的對象
FutureTask task = new FutureTask<>(callable);
new Thread(task).start();
// 買食材
FoodMaterial fm = new FoodMaterial();
Thread.sleep(2000);
System.out.println("第二步: 食材已經(jīng)到位");
if (!task.isDone()) {
System.out.println("廚具還沒有到.....");
}
// 通過阻塞形式獲取到異步塊執(zhí)行的結(jié)果
KitchenWare kc = task.get(); // 阻塞
// 烹飪美食
cooking(kc, fm);
System.out.println("第三步: 美食烹飪完成");
long end = System.currentTimeMillis();
System.out.println("烹飪美食時間為:" + (end - start));
}
/**
* 廚具類
* @author Administrator
*
*/
static class KitchenWare {
}
/**
* 食材類
* @author Administrator
*
*/
static class FoodMaterial {
}
/**
* 定義烹飪食物的方法
* @param kc
* @param fm
*/
static void cooking(KitchenWare kc, FoodMaterial fm) {
}
}
執(zhí)行結(jié)果:
第一步: 網(wǎng)上下單
第一步: 等待廚具
第二步: 食材已經(jīng)到位
廚具還沒有到.....
第一步: 快遞送貨
第三步: 美食烹飪完成
烹飪美食時間為:5027
這個是JDK5中就有的 Future 來實(shí)現(xiàn) 異步編程的,那么接下來我們看1.8的異步編程。
CompletableFuture
Future 雖然可以實(shí)現(xiàn)獲取異步執(zhí)行結(jié)果的需求,但是它沒有提供通知的機(jī)制,我們無法得知Future什么時候完成,我們通過上面的代碼也完全能看出來。
為什么在JDK5之后,又推出新的異步編程,因?yàn)槭褂?Future 要么使用阻塞,在 future.get() 的地方等待 Future 返回的結(jié)果,這時又變成同步操作。要么使用 isDone() 輪詢地判斷 Future 是否完成,這樣會耗費(fèi)CPU的資源。所以阿粉猜測所以在JDK8又推出了 CompletableFuture。
之前 Future 需要等待 isDone 為 true 才能知道任務(wù)跑完了?;蛘呔褪怯?get 方法調(diào)用的時候會出現(xiàn)阻塞。而使用 CompletableFuture 的使用就可以用 then , when 等等操作來防止以上的阻塞和輪詢 isDone 的現(xiàn)象出現(xiàn)。
CompletableFuture 有四個方法來創(chuàng)建CompletableFuture對象。
public static CompletableFuturerunAsync(Runnable runnable)
public static CompletableFuturerunAsync(Runnable runnable, Executor executor)
public static CompletableFuture supplyAsync(Supplier supplier)
public static CompletableFuture supplyAsync(Supplier supplier, Executor executor)
Asynsc表示異步,而supplyAsync與runAsync不同在與前者異步返回一個結(jié)果,后者是void.第二個函數(shù)第二個參數(shù)表示是用我們自己創(chuàng)建的線程池,否則采用默認(rèn)的ForkJoinPool.commonPool()作為它的線程池.其中Supplier是一個函數(shù)式接口,代表是一個生成者的意思,傳入0個參數(shù),返回一個結(jié)果。
我們寫一個最簡單的測試代碼:
public static void test2() throws Exception {
//supplyAsync內(nèi)部使用ForkJoinPool線程池執(zhí)行任務(wù)
CompletableFuture completableFuture=CompletableFuture.supplyAsync(()->{
//模擬執(zhí)行耗時任務(wù)
System.out.println("task doing...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//返回結(jié)果
return "100";
}).whenComplete((v,r)->{
System.out.println("計算結(jié)果是: "+v);
});
//CompletableFuture里使用的線程池里的線程默認(rèn)是daemon的。main線程結(jié)束后,整個程序也
//結(jié)束了,這里將main線程join后任務(wù)里的代碼才可以執(zhí)行完
Thread.currentThread().join();
}
而使用 CompletableFuture 能有效的避開使用 Futrue 出現(xiàn)的缺點(diǎn)。
看來,JDK 每一次的更新?lián)Q代,不光是加了一些新的內(nèi)容,而且像開發(fā)一樣,每次迭代的時候,同時也會更新之前的一些不完美的內(nèi)容,不是么?
分享文章:異步編程還得看JDK8
本文路徑:http://www.5511xx.com/article/dpijohh.html


咨詢
建站咨詢
