Table of Contents
ToggleCancel a Task in Java ExecutorService
當任務由 ExecutorService 執行緒池執行時,可以使用允許發出取消請求的 Future.cancel 方法,刪除佇列中的任務,若將 mayInterruptIfRunning 參數設為 true 時,可能可以中斷正在運行的任務,因此任務必須定期檢查中斷狀態, Cancel Java ExecutorService 本篇增加了範例,並透過單元測試來驗證產出結果。
檔案目錄
./
+- src
+- test
| +- org
| +- ruoxue
| +- java_147
| +- multithreading
| +- executorservice
| +- ExecutorServiceCancelTest.java
單元測試
ExecutorService Cancel Java 提供取消或中斷任務執行。
fibonacci
建立一個費氏數列方法,使用遞迴運算,並將結果傳回,約在參數傳入 45 以上的值後,就會明顯地耗時執行。
protected class FibonacciWorker implements Callable<Long> {
int input = 0;
public FibonacciWorker() {
}
public FibonacciWorker(int input) {
this.input = input;
}
public Long call() {
return calculate(input);
}
protected long calculate(int n) {
try {
if (Thread.currentThread().isInterrupted()) {
System.out.println("stop calculate");
throw new InterruptedException();
}
if (n <= 1)
return n;
else
return calculate(n - 1) + calculate(n - 2);
} catch (InterruptedException ex) {
ex.printStackTrace();
Thread.currentThread().interrupt();
}
return 0;
}
}
@Test
public void fibonacci() {
for (int i = 0; i < 5; i++) {
System.out.println("f(" + i + ") = " + new FibonacciWorker(i).call());
}
}
f(0) = 0
f(1) = 1
f(2) = 1
f(3) = 2
f(4) = 3
cancelFalse
ExecutorService Cancel Java 建立一個執行緒池,固定數量 3 條執行緒,執行 1 個任務,主執行緒取得每個任務結束後的傳回值,若 3 秒後若取不到值,則會拋出例外結束等待,並且發送 cancel 取消任務執行。
@Test
public void cancelFalse() {
int poolSize = 3;
ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
Future<Long> future = executorService.submit(new FibonacciWorker(47));
long result = -1;
try {
result = future.get(3, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
} catch (TimeoutException ex) {
ex.printStackTrace();
future.cancel(false);
}
System.out.println(result);
}
java.util.concurrent.TimeoutException
-1
at java.util.concurrent.FutureTask.get(FutureTask.java:205)
at org.ruoxue.java_147.multithreading.ExecutorServiceCancelTest.cancelFalse(ExecutorServiceCancelTest.java:62)
cancelTrue
ExecutorService Cancel Java 建立一個執行緒池,固定數量 3 條執行緒,執行 1 個任務,主執行緒取得每個任務結束後的傳回值,若 3 秒後若取不到值,則會拋出例外結束等待,並且發送 cancel 取消任務執行,任務內必須檢查執行緒是否被中斷 isInterrupted ,若為真則拋出 InterruptedException 例外,中斷任務。
@Test
public void cancelTrue() {
int poolSize = 3;
ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
Future<Long> future = executorService.submit(new FibonacciWorker(47));
long result = -1;
try {
result = future.get(3, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
} catch (TimeoutException ex) {
ex.printStackTrace();
future.cancel(true);
}
System.out.println(result);
}
java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask.get(FutureTask.java:205)
at org.ruoxue.java_147.multithreading.ExecutorServiceCancelTest.cancelTrue(ExecutorServiceCancelTest.java:79)
-1
java.lang.InterruptedException
stop calculate
at org.ruoxue.java_147.multithreading.ExecutorServiceCancelTest$FibonacciWorker.calculate(ExecutorServiceCancelTest.java:34)
at org.ruoxue.java_147.multithreading.ExecutorServiceCancelTest$FibonacciWorker.calculate(ExecutorServiceCancelTest.java:39)
at org.ruoxue.java_147.multithreading.ExecutorServiceCancelTest$FibonacciWorker.calculate(ExecutorServiceCancelTest.java:39)
ExecutorServiceCancelTest.java
ExecutorService Cancel Example 新增單元測試,驗證是否符合預期。
package org.ruoxue.java_147.multithreading.executorservice;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.Test;
public class ExecutorServiceCancelTest {
protected class FibonacciWorker implements Callable<Long> {
int input = 0;
public FibonacciWorker() {
}
public FibonacciWorker(int input) {
this.input = input;
}
public Long call() {
return calculate(input);
}
protected long calculate(int n) {
try {
if (Thread.currentThread().isInterrupted()) {
System.out.println("stop calculate");
throw new InterruptedException();
}
if (n <= 1)
return n;
else
return calculate(n - 1) + calculate(n - 2);
} catch (InterruptedException ex) {
ex.printStackTrace();
Thread.currentThread().interrupt();
}
return 0;
}
}
@Test
public void fibonacci() {
for (int i = 0; i < 5; i++) {
System.out.println("f(" + i + ") = " + new FibonacciWorker(i).call());
}
}
@Test
public void cancelFalse() {
int poolSize = 3;
ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
Future<Long> future = executorService.submit(new FibonacciWorker(47));
long result = -1;
try {
result = future.get(3, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
} catch (TimeoutException ex) {
ex.printStackTrace();
future.cancel(false);
}
System.out.println(result);
}
@Test
public void cancelTrue() {
int poolSize = 3;
ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
Future<Long> future = executorService.submit(new FibonacciWorker(47));
long result = -1;
try {
result = future.get(3, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
} catch (TimeoutException ex) {
ex.printStackTrace();
future.cancel(true);
}
System.out.println(result);
}
}
心得分享
Java ExecutorService Cancel 取消或中斷任務執行,尤其當有耗時的任務執行時,想要中途中斷任務,就可以使用 Future.cancel 傳入 true ,此時會調用 Thread.currentThread().interrupt() 標示為中斷,目前執行任務的執行緒可能會中斷,而在任務內則需要定期檢查中斷狀態,處理任務中斷的工作, ExecutorService Cancel Example 提供了幾種常見方法的操作範例。