CompletableFuture不能被打断
我已經寫了很多有關InterruptedException和中斷線程的文章 。 簡而言之,如果您沒有Future.cancel()調用Future.cancel()那么Future將終止待處理的get() ,但還將嘗試中斷基礎線程。 這是一個非常重要的功能,可以更好地利用線程池。 我還寫信總是比標準Future更喜歡CompletableFuture 。 事實證明,功能更強大的Future弟兄沒有那么優雅地處理cancel() 。 考慮以下任務,我們稍后將在整個測試中使用以下任務:
class InterruptibleTask implements Runnable {private final CountDownLatch started = new CountDownLatch(1)private final CountDownLatch interrupted = new CountDownLatch(1)@Overridevoid run() {started.countDown()try {Thread.sleep(10_000)} catch (InterruptedException ignored) {interrupted.countDown()}}void blockUntilStarted() {started.await()}void blockUntilInterrupted() {assert interrupted.await(1, TimeUnit.SECONDS)}}客戶端線程可以檢查InterruptibleTask以查看其是否已開始或被中斷。 首先,讓我們看一下InterruptibleTask對外部的cancel()反應:
def "Future is cancelled without exception"() {given:def task = new InterruptibleTask()def future = myThreadPool.submit(task)task.blockUntilStarted()and:future.cancel(true)when:future.get()then:thrown(CancellationException) }def "CompletableFuture is cancelled via CancellationException"() {given:def task = new InterruptibleTask()def future = CompletableFuture.supplyAsync({task.run()} as Supplier, myThreadPool)task.blockUntilStarted()and:future.cancel(true)when:future.get()then:thrown(CancellationException) }到目前為止,一切都很好。 顯然, Future和CompletableFuture工作方式幾乎相同-在取消結果后檢索結果會引發CancellationException 。 但是myThreadPool線程呢? 我以為它會被游泳池打斷,從而被回收,我怎么了!
def "should cancel Future"() {given:def task = new InterruptibleTask()def future = myThreadPool.submit(task)task.blockUntilStarted()when:future.cancel(true)then:task.blockUntilInterrupted() }@Ignore("Fails with CompletableFuture") def "should cancel CompletableFuture"() {given:def task = new InterruptibleTask()def future = CompletableFuture.supplyAsync({task.run()} as Supplier, myThreadPool)task.blockUntilStarted()when:future.cancel(true)then:task.blockUntilInterrupted() }第一個測試將普通的Runnable提交給ExecutorService并等待其啟動。 稍后,我們取消Future并等待直到觀察到InterruptedException 。 當基礎線程被中斷時, blockUntilInterrupted()將返回。 但是,第二次測試失敗。 CompletableFuture.cancel()永遠不會中斷基礎線程,因此盡管Future看起來好像已被取消,但后備線程仍在運行,并且sleep()不會拋出InterruptedException 。 錯誤或功能? 它已記錄在案 ,因此很遺憾地提供一個功能:
參數: mayInterruptIfRunning –此值在此實現中無效,因為不使用中斷來控制處理。
您說RTFM,但是為什么CompletableFuture這樣工作? 首先,讓我們研究“舊的” Future實現與CompletableFuture有何不同。 從ExecutorService.submit()返回的FutureTask具有以下cancel()實現(我使用類似的非線程安全Java代碼刪除了Unsafe ,因此僅將其視為偽代碼):
public boolean cancel(boolean mayInterruptIfRunning) {if (state != NEW)return false;state = mayInterruptIfRunning ? INTERRUPTING : CANCELLED;try {if (mayInterruptIfRunning) {try {Thread t = runner;if (t != null)t.interrupt();} finally { // final statestate = INTERRUPTED;}}} finally {finishCompletion();}return true; }FutureTask具有一個遵循該狀態圖的state變量:
在的情況下, cancel()我們可以進入CANCELLED狀態或去INTERRUPTED通過INTERRUPTING 。 核心部分是我們拿runner線程(如果存在的,也就是說,如果當前正在執行的任務),我們盡量打斷它。 該分支負責急切和強制中斷已運行的線程。 最后,我們必須通知阻塞的所有線程Future.get()中finishCompletion()這里無關緊要)。 因此,很明顯,多大的Future取消已經運行的任務。 那CompletableFuture呢? cancel()偽代碼:
public boolean cancel(boolean mayInterruptIfRunning) {boolean cancelled = false;if (result == null) {result = new AltResult(new CancellationException());cancelled = true;}postComplete();return cancelled || isCancelled(); }令人失望的是,我們幾乎沒有將result設置為CancellationException ,而忽略了mayInterruptIfRunning標志。 postComplete()也有類似的作用finishCompletion() -在通知未來注冊的所有懸而未決的回調。 它的實現相當令人不快(使用非阻塞式Treiber stack ),但是它絕對不會中斷任何底層線程。
原因和含義
在CompletableFuture情況下,有限的cancel()不是錯誤,而是設計決定。 CompletableFuture并非固有地綁定到任何線程,而Future幾乎總是代表后臺任務。 從零開始創建CompletableFuture ( new CompletableFuture<>() )是完美的,其中根本沒有要取消的底層線程。 我仍然不禁感到大多數CompletableFuture 都將具有關聯的任務和后臺線程。 在這種情況下, cancel()可能會出現故障。 我不再建議用CompletableFuture盲目地替換Future ,因為它可能會更改依賴cancel()的應用程序的行為。 這意味著CompletableFuture故意違反了Liskov替換原則 -這是需要考慮的嚴重問題。
翻譯自: https://www.javacodegeeks.com/2015/03/completablefuture-cant-be-interrupted.html
總結
以上是生活随笔為你收集整理的CompletableFuture不能被打断的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用tinylog 1.0简化您的日志记
- 下一篇: win10qq远程控制电脑(qq如何远程