碼農在囧途
生活是一個洗禮自己的過程,這個洗禮並不是傳統意義上的洗禮,傳統意義上的洗禮通常認為這個人的思想得到洗禮,靈魂得到洗禮,十分的清新脫俗,不世故,不圓滑,而現實的洗禮實則是讓一個人褪去幼稚,褪去無知,讓你變得點頭哈腰,圓滑世故,我們都是動物,需要物質滿足,更需要慾望填補,所以,變成自己小時候唾罵的物件也是可以理解,不過這是一個選擇,你可以進行選擇,只是在物慾橫流的時代,多數人沒有這種選擇的權力!
Future和FutureTask
Future
是一個介面,FutureTask
是一個類,實現RunnableFuture
介面,RunnableFuture
介面繼承Future
介面。
Future介面的方法
V get() :獲取非同步執行的結果,如果沒有返回結果,此方法會阻塞直到非同步計算完成。
V get(Long timeout , TimeUnit unit) :獲取非同步執行結果,如果沒有結果可用,此方法會阻塞,但是會有時間限制,如果阻塞時間超過設定的timeout時間,該方法將丟擲異常。
boolean isDone() :如果任務執行結束,無論是正常結束或是中途取消還是發生異常,都返回true。
boolean isCancelled() :如果任務完成前被取消,則返回true。
boolean cancel(boolean mayInterruptRunning) :如果任務還沒開始,執行cancel(...)方法將返回false;如果任務已經啟動, 執行cancel(true)方法將以中斷執行此任務執行緒的方式來試圖停止任務,如果停止成功,返回true;當任務已經啟動, 執行cancel(false)方法將不會對正在執行的任務執行緒產生影響(讓執行緒正常執行到完成),此時返回false;當任務已經完成, 執行cancel(...)方法將返回false。mayInterruptRunning參數列示是否中斷執行中的執行緒。
Future
是一個介面,因此我們不能直接建立物件,需要配合執行緒池一起使用,FutureTask
我們可以直接建立物件。
Future的使用
Future
代表非同步執行的結果,也就是說非同步執行完畢後,結果儲存在Future
裡, 我們在使用執行緒池submit()
時需要傳入Callable
介面,執行緒池的返回值為一個Future
,而Future
則儲存了執行的結果 ,可通過Future
的get()
方法取出結果,如果執行緒池使用的是execute()
方法,則傳入的是Runnable
介面無返回值。
如下我們使用Future
模擬下單操作,使用者下單後儲存訂單資訊
,扣減庫存
,增加積分
,傳送簡訊通知
,這麼多個任務如果使用同步執行,那麼效率就會 比較低,使用者體驗不好,一般我們會採用訊息佇列
來達到非同步的效果,今天我們就不用訊息佇列,而是使用Future
介面來實現非同步。
public class FutureTest {
final static ExecutorService threadPool = Executors.newCachedThreadPool();
//儲存訂單任務
public static Future<R> saveOrderTask(OrderInfo orderInfo) {
return threadPool.submit(new Callable<R>() {
@Override
public R call() throws Exception {
return saveOrder(orderInfo);
}
});
}
//扣減庫存任務
public static Future<R> decreaseStockTask(OrderInfo orderInfo) {
return threadPool.submit(new Callable<R>() {
@Override
public R call() throws Exception {
return decreaseStockByCommodityId(orderInfo);
}
});
}
//增加積分任務
public static Future<R> increaseIntegralTask(OrderInfo orderInfo) {
return threadPool.submit(new Callable<R>() {
@Override
public R call() throws Exception {
return increaseIntegralByUserId(orderInfo);
}
});
}
public static void sendMsgToPhone(OrderInfo orderInfo) {
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("使用者【" + orderInfo.getUserId() + "】,你已下單成功~~~~~~~~");
}
});
}
//增加積分rpc介面
public static R increaseIntegralByUserId(OrderInfo orderInfo) {
System.out.println("增加積分~~~~~~~~");
integralService.increaseIntegralByUserId(orderInfo.getUserId(),20);
return new R(200, "增加積分成功", null);
}
//扣減庫存rpc介面
public static R decreaseStockByCommodityId(OrderInfo orderInfo) {
System.out.println("扣減庫存~~~~~~~~");
stockService.decreaseStockByCommodityId(orderInfo.getCommodityId());
return new R(200, "扣減庫存成功", null);
}
//儲存訂單rpc介面
public static R saveOrder(OrderInfo orderInfo) throws InterruptedException {
System.out.println("儲存訂單~~~~~~~~");
Thread.sleep(2000);
orderService.insert(orderInfo);
return new R(200, "儲存訂單成功", null);
}
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
OrderInfo orderInfo = new OrderInfo().setId("123455").setUserId("111111").setCommodityId("123321");
Future<R> orderTask = saveOrderTask(orderInfo);
Future<R> stockTask = decreaseStockTask(orderInfo);
Future<R> integralTask = increaseIntegralTask(orderInfo);
sendMsgToPhone(orderInfo);
if (orderTask.get().getCode() == 200 && orderTask.isDone())
System.out.println(orderTask.get().getMsg());
if (stockTask.get().getCode() == 200 && stockTask.isDone())
System.out.println(stockTask.get().getMsg());
if (integralTask.get().getCode() == 200 && integralTask.isDone())
System.out.println(integralTask.get().getMsg());
threadPool.shutdownNow();
}
}
輸出
儲存訂單~~~~~~~~
扣減庫存~~~~~~~~
增加積分~~~~~~~~
使用者【111111】,你已下單成功~~~~~~~~
儲存訂單成功
扣減庫存成功
增加積分成功
我們在儲存訂單介面模擬處理業務操作,花費了2s
,從輸出結果可以看出,其他rpc
介面並沒有在儲存訂單時而阻塞,而是同時執行,就達到了非同步的效果。
不過我們發現了一個問題,那就是非同步返回結果被阻塞了,我明明我扣減庫存和增加積分介面很快就返回,但是從輸出中卻發現扣減庫存和增加積分在儲存 訂單後輸出,由此我們可看出Future
會阻塞返回結果。
上面我們傳送簡訊到使用者手機並沒有獲取返回結果,所以沒有使用Future
,使用執行緒池我們就沒有使用Callable
介面,而是使用Runnable
介面, 方法就是execute()
,而不是submit()
execute()和submit()區別
1.execute
無返回值,這樣就無法知道任務是否執行成功,而submit
有返回值。 2.execute
丟擲異常後無法處理,不能捕捉異常,而submit
可以捕獲異常;
FutureTask的使用
FutureTask
時Future
介面的實現類,我們可以直接建立一個FutureTask
物件,下面我們對上面的下單流程就行改造,使用FutureTask
來實現。
/**
* @author 劉牌
* @date 2022/3/2617:34
*/
public class PlaceOrderFutureTaskTest {
final static ExecutorService threadPool = Executors.newCachedThreadPool();
//儲存訂單任務
public static FutureTask<R> saveOrderTask(OrderInfo orderInfo){
return new FutureTask<>(new Callable<R>() {
@Override
public R call() throws Exception {
return saveOrder(orderInfo);
}
});
}
//扣減庫存任務
public static FutureTask<R> decreaseStockTask(OrderInfo orderInfo){
return new FutureTask<>(new Callable<R>() {
@Override
public R call() throws Exception {
return decreaseStockByCommodityId(orderInfo);
}
});
}
//增加積分任務
public static FutureTask<R> increaseIntegralTask(OrderInfo orderInfo){
return new FutureTask<>(new Callable<R>() {
@Override
public R call() throws Exception {
return increaseIntegralByUserId(orderInfo);
}
});
}
public static void sendMsgToPhone(OrderInfo orderInfo){
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("使用者【"+orderInfo.getUserId()+"】,你已下單成功~~~~~~~~");
}
});
}
//增加積分rpc介面
public static R increaseIntegralByUserId(OrderInfo orderInfo){
System.out.println("增加積分~~~~~~~~");
integralService.increaseIntegralByUserId(orderInfo.getUserId(),20);
return new R(200,"增加積分成功",null);
}
//扣減庫存rpc介面
public static R decreaseStockByCommodityId(OrderInfo orderInfo){
System.out.println("扣減庫存~~~~~~~~");
stockService.decreaseStockByCommodityId(orderInfo.getCommodityId());
return new R(200,"扣減庫存成功",null);
}
//儲存訂單rpc介面
public static R saveOrder(OrderInfo orderInfo) throws InterruptedException {
System.out.println("儲存訂單~~~~~~~~");
Thread.sleep(2000);
orderService.insert(orderInfo);
return new R(200,"儲存訂單成功",null);
}
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
OrderInfo orderInfo = new OrderInfo().setId("123455").setUserId("111111").setCommodityId("123321");
FutureTask<R> orderTask = saveOrderTask(orderInfo);
FutureTask<R> stockTask = decreaseStockTask(orderInfo);
FutureTask<R> integralTask = increaseIntegralTask(orderInfo);
threadPool.submit(orderTask);
threadPool.submit(stockTask);
threadPool.submit(integralTask);
sendMsgToPhone(orderInfo);
if (orderTask.get().getCode() == 200 && orderTask.isDone())
System.out.println(orderTask.get().getMsg());
if (stockTask.get().getCode() == 200 && stockTask.isDone())
System.out.println(stockTask.get().getMsg());
if (integralTask.get().getCode() == 200 && integralTask.isDone())
System.out.println(integralTask.get().getMsg());
threadPool.shutdownNow();
}
}
輸出
儲存訂單~~~~~~~~
扣減庫存~~~~~~~~
增加積分~~~~~~~~
使用者【111111】,你已下單成功~~~~~~~~
儲存訂單成功
扣減庫存成功
增加積分成功
額~~~,從程式碼中我們看出其實沒啥區別,就是一個介面和實現類的不同寫法而已,從輸入也可以看出和上面的Future
一樣,由此可知FutureTask
獲取結果也是 阻塞的。
總結
從上面的流程中可以看出,Future
和FutureTask
能夠實現非同步,但是獲取結果卻是同步的,這缺陷也是顯而易見,如果遇到耗時的任務,那麼獲取返回值的時候 其他任務就會被阻塞,只能排隊慢慢來,在高併發的場景下不適合,那有沒有解決方案呢,肯定有的,那就是CompletableFuture
,我們後面繼續介紹,本章我們 就不對其進行介紹。
今天的分享就到這裡,感謝你的觀看,我們下期見。