java 多線程 合並多個查詢結果


場景:假如你突然想做飯,但是沒有廚具,也沒有食材。網上購買廚具比較方便,食材去超市買更放心。

實現分析:在快遞員送廚具的期間,我們肯定不會閑着,可以去超市買食材。所以,在主線程里面另起一個子線程去網購廚具。

但是,子線程執行的結果是要返回廚具的,而run方法是沒有返回值的。所以,這才是難點,需要好好考慮一下。

模擬代碼:

 1 package test;
 2 
 3 import java.util.concurrent.Callable;
 4 import java.util.concurrent.ExecutionException;
 5 import java.util.concurrent.FutureTask;
 6 
 7 public class FutureCook {
 8 
 9     public static void main(String[] args) throws InterruptedException, ExecutionException {
10         long startTime = System.currentTimeMillis();
11         // 第一步 網購廚具
12         Callable<Chuju> onlineShopping = new Callable<Chuju>() {
13 
14             @Override
15             public Chuju call() throws Exception {
16                 System.out.println("第一步:下單");
17                 System.out.println("第一步:等待送貨");
18                 Thread.sleep(5000);  // 模擬送貨時間
19                 System.out.println("第一步:快遞送到");
20                 return new Chuju();
21             }
22             
23         };
24         FutureTask<Chuju> task = new FutureTask<Chuju>(onlineShopping);
25         new Thread(task).start();
26         // 第二步 去超市購買食材
27         Thread.sleep(2000);  // 模擬購買食材時間
28         Shicai shicai = new Shicai();
29         System.out.println("第二步:食材到位");
30         // 第三步 用廚具烹飪食材
31         if (!task.isDone()) {  // 聯系快遞員,詢問是否到貨
32             System.out.println("第三步:廚具還沒到,心情好就等着(心情不好就調用cancel方法取消訂單)");
33         }
34         Chuju chuju = task.get();
35         System.out.println("第三步:廚具到位,開始展現廚藝");
36         cook(chuju, shicai);
37         
38         System.out.println("總共用時" + (System.currentTimeMillis() - startTime) + "ms");
39     }
40     
41     //  用廚具烹飪食材
42     static void cook(Chuju chuju, Shicai shicai) {}
43     
44     // 廚具類
45     static class Chuju {}
46     
47     // 食材類
48     static class Shicai {}
49 
50 }

結果

1 第一步:下單
2 第一步:等待送貨
3 第二步:食材到位
4 第三步:廚具還沒到,心情好就等着(心情不好就調用cancel方法取消訂單)
5 第一步:快遞送到
6 第三步:廚具到位,開始展現廚藝
7 總共用時5005ms

下面具體分析一下這段代碼:

1)把耗時的網購廚具邏輯,封裝到了一個Callable的call方法里面。

復制代碼
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}
復制代碼

 Callable接口可以看作是Runnable接口的補充,call方法帶有返回值,並且可以拋出異常。

 

2)把Callable實例當作參數,生成一個FutureTask的對象,然后把這個對象當作一個Runnable,作為參數另起線程。

public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>
復制代碼
public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}
復制代碼

這個繼承體系中的核心接口是Future。Future的核心思想是:一個方法f,計算過程可能非常耗時,等待f返回,顯然不明智。可以在調用f的時候,立馬返回一個Future,可以通過Future這個數據結構去控制方法f的計算過程。

這里的控制包括:

get方法:獲取計算結果(如果還沒計算完,也是必須等待的)

cancel方法:還沒計算完,可以取消計算過程

isDone方法:判斷是否計算完

isCancelled方法:判斷計算是否被取消

這些接口的設計很完美,FutureTask的實現注定不會簡單,后面再說。

 

3)在第三步里面,調用了isDone方法查看狀態,然后直接調用task.get方法獲取廚具,不過這時還沒送到,所以還是會等待3秒。對比第一段代碼的執行結果,這里我們節省了2秒。這是因為在快遞員送貨期間,我們去超市購買食材,這兩件事在同一時間段內異步執行!!!

 

JDK8 中 CompletableFuture 是非常強大的 Future 的擴展功能。

模擬代碼:

public class CompleteFutureTests {
    public static void main(String[] args) throws Exception {
//1
CompletableFuture<String> bookFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("000000000");
return "xanyi000001";
});

//2
CompletableFuture<String> tableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("沉睡5秒");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "xanyi111110";
});

//CompletableFuture.allOf(bookFuture, tableFuture).join();
CompletableFuture.anyOf(bookFuture, tableFuture).join();

System.out.println("book -> " + bookFuture.get());
System.out.println("tale -> " + tableFuture.get());
}
}

allOf 工廠方法接收一個由CompletableFuture 構成的數組,數組中的所有 Completable-Future 對象執行完成之后,它返回一個 CompletableFuture<Void> 對象。這意味着,如果你需要等待多個 CompletableFuture 對象執行完畢,對 allOf 方法返回的
CompletableFuture 執行 join 操作可以等待CompletableFuture執行完成。

或者你可能希望只要 CompletableFuture 對象數組中有任何一個執行完畢就不再等待,在這種情況下,你可以使用一個類似的工廠方法 anyOf

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM