2016-11-03 74 views
2

在Java中使用CompletableFuture時遇到問題。 我有2個選擇請求,這些請求在從服務器接收響應時填充。Java CompletableFuture.complete()塊

在連接線程(線程1)(使用反應器)中,我使用:

if(hasException) { 
    selectFuture.completeExceptionally(new ClientException(errorCode)); 
} else { 
    System.out.println("Before complete future"); 
    selectFuture.complete(result); 
    System.out.println("After complete future"); 
} 

而在其他線程(線程2),我使用:

CompleteFuture.allOf(allSelect).whenComplete((aVoid, throwable) -> { 
    System.out.println("Receive all future"); 
    // Do sth here 
}); 

我的情況是系統打印出「接收所有未來」,但打電話時屏蔽THREAD-1 future.complete(result);它不能脫離該命令。 如果在THREAD-2中,我使用CompletableFuture.allOf(allOfSelect).get(),則THREAD-1將正確運行。但是使用CompletableFuture.get()會降低性能,所以我想用CompletableFuture.whenComplete()

任何人都可以幫我解釋阻塞的原因嗎?

謝謝!

回答

2

complete調用觸發所有依賴CompletionStage s。

因此,如果您之前註冊了BiConsumerwhenCompletecomplete將在其調用線程中調用它。在你的情況下,當BiConsumer傳遞給whenComplete時,complete的呼叫將返回。這在對非異步方法依賴完井可以 通過即完成當前 CompletableFuture,或通過完成方法的任何其他呼叫者線程執行供給的所述class javadoc

操作說明。

由另一呼叫者是相反的情況,即線程調用whenComplete實際上應用BiConsumer如果目標CompletableFuture已經完成。)

這裏有一個小程序來說明行爲:

public static void main(String[] args) throws Exception { 
    CompletableFuture<String> future = new CompletableFuture<String>(); 
    future.whenComplete((r, t) -> { 
     System.out.println("before sleep, executed in thread " + Thread.currentThread()); 
     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     System.out.println("after sleep, executed in thread " + Thread.currentThread()); 
    }); 

    System.out.println(Thread.currentThread()); 
    future.complete("completed"); 
    System.out.println("done"); 
} 

這將打印

Thread[main,5,main] 
before sleep, executed in thread Thread[main,5,main] 
after sleep, executed in thread Thread[main,5,main] 
done 

顯示在主線程中應用了BiConsumer,這個主線程叫做complete

您可以使用whenCompleteAsync強制在單獨的線程中執行BiConsumer

[...]執行時,這個階段完成使用此階段的默認 異步執行機構指定的動作

例如,

public static void main(String[] args) throws Exception { 
    CompletableFuture<String> future = new CompletableFuture<String>(); 
    CompletableFuture<?> done = future.whenCompleteAsync((r, t) -> { 
     System.out.println("before sleep, executed in thread " + Thread.currentThread()); 
     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     System.out.println("after sleep, executed in thread " + Thread.currentThread()); 
    }); 

    System.out.println(Thread.currentThread()); 
    future.complete("completed"); 
    System.out.println("done"); 
    done.get(); 
} 

將打印

Thread[main,5,main] 
done 
before sleep, executed in thread Thread[ForkJoinPool.commonPool-worker-1,5,main] 
after sleep, executed in thread Thread[ForkJoinPool.commonPool-worker-1,5,main] 

表明該BiConsumer在單獨的線程施加。

+0

非常感謝。你的解釋非常清楚線程。 在我第一次使用CompletableFuture時,我只使用它來調用從客戶端到服務器的單個請求(如SELECT,UPDATE等),所以它完全按照我的期望(客戶端等待響應並填補未來)運行。 但是現在,我在做TRANSACTION時遇到問題,因爲它有階段。我希望在所有SELECT完成後立即作爲CompletableFuture返回未來,但只有在其他執行(UPDATE,DELETE)有結果時才填充未來。 你能建議我一個解決方案來處理鏈中的事情嗎? 非常感謝! –

+0

我想按以下順序做事: 1. SELECT - > CompletableFuture 2. PREPARE - > CompletableFuture(其他的調用也會返回CompletableFutures)。 3. COMMIT - > CompletableFuture 並且當我註冊它時,事務必須立即返回給調用者。 非常感謝! –