Cancel a Task in Java ExecutorService - Java 147

Cancel a Task in Java ExecutorService – Java 147

Cancel 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 提供了幾種常見方法的操作範例。

發佈留言