1. 程式人生 > >JAVA併發程式設計(四)任務的取消與關閉

JAVA併發程式設計(四)任務的取消與關閉

概述

之前三篇文章介紹了多執行緒的同步,加鎖機制,同步容器,併發容器,以及多執行緒工具類,執行緒池等。

大多數時候,我們會讓執行緒執行直到結束。然而,有時候我們希望提前結束任務,或者因為使用者取消了操作,需要終止執行緒。我們來看一下如何使一個執行緒安全、快速、可靠的停下來。

任務取消的原因

1、使用者主動取消。如點選圖形介面的取消按鈕等。

2、有時間限制的操作。當超過時間限制時,需要結束任務。

3、應用程式事件。當一個任務找到解決方案時,需要結束操作。

4、出現錯誤。當任務執行過程中出現不可恢復的錯誤,需要結束任務。

5、關閉。當一個程式被關閉時,需要做一些清理操作。

使用簡單標誌位儲存取消狀態

為了保證標誌位可靠,標誌必須為volatile型別。
/**
 * 
 * @author chao
 *
 */
public class SafeCancel extends Thread {
	private volatile boolean cancelled;

	@Override
	public void run() {
		while (!cancelled) {
			// todo something
		}
	}

	public void cancel() {
		cancelled = true;
	}
}
一個可取消的任務必須擁有取消策略。這個策略需要詳細定義如何取消改任務,何時檢查是否已經取消以及在響應取消請求之後該執行哪些操作。

中斷

使用標誌位來取消任務是不及時的,如果中間呼叫了阻塞方法,有可能永遠都無法結束。
/**
 * 
 * @author chao
 *
 */
class InterruptCancel extends Thread {
	BlockingQueue<Object> queue = new LinkedBlockingQueue<>();

	@Override
	public void run() {
		try {
			while (!isInterrupted()) {
				Object info = new Object();
				// do something
				queue.put(info);
			}
		} catch (InterruptedException e) {
		}
	}

	public void cancel() {
		interrupt();
	}
}
每個執行緒都有一個boolean型別的中斷狀態,interrupt方法能中斷目標執行緒,而isInterrupted方法能返回目標執行緒的中斷狀態,靜態的interrupted方法將清除當前執行緒的中斷狀態,也是清除中斷狀態的唯一一個方法。

中斷響應

當呼叫可中斷的阻塞方法時,有兩種策略可用於處理InterruptedException1、傳遞異常,從而使你的方法也成為可中斷的阻塞方法。2、恢復中斷狀態,從而使呼叫棧上中的上層程式碼能夠對其進行處理。只有實現了執行緒中斷策略的程式碼才可以遮蔽中斷請求,常規任務中不應該遮蔽中斷請求。

通過Future實現取消

private static ExecutorService service = Executors.newCachedThreadPool();

	public static void timedRun(Runnable r, long timeout, TimeUnit unit) {
		Future<?> task = service.submit(r);
		try {
			task.get(timeout, unit);
		} catch (InterruptedException | ExecutionException | TimeoutException e) {

		} finally {
			task.cancel(true);
		}
	}

關閉ExecutorService

ExecutorService提供了兩種關閉方法:使用shutdown正常關閉,使用shutdownNow強行關閉。在進行強行關閉時,首先關閉當前正在進行的任務,然後返回所有尚未啟動的任務清單。

處理未捕獲的異常

Thread API提供了UncaughtExceptionHandler,檢測出某個執行緒由於未捕獲異常而終結的情況。在Android開發中,可以用來上報異常,發現問題及時修復。Thread的方法
  public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler paramUncaughtExceptionHandler)
  {
    SecurityManager localSecurityManager = System.getSecurityManager();
    if (localSecurityManager != null) {
      localSecurityManager.checkPermission(new RuntimePermission("setDefaultUncaughtExceptionHandler"));
    }
    defaultUncaughtExceptionHandler = paramUncaughtExceptionHandler;
  }
  
  
  public static abstract interface UncaughtExceptionHandler
  {
    public abstract void uncaughtException(Thread paramThread, Throwable paramThrowable);
  }
  

Android中提供了另一個方法,單獨設定每個執行緒的未捕獲異常處理,更加靈活。
 public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
        uncaughtHandler = handler;
    }

JVM關閉鉤子

在線上Java程式中經常遇到程序程掛掉,一些狀態沒有正確的儲存下來,這時候就需要在JVM關掉的時候執行一些清理現場的程式碼。Java中得ShutdownHook提供了比較好的方案。
  JDK在1.3之後提供了Java Runtime.addShutdownHook(Thread hook)方法,可以註冊一個JVM關閉的鉤子,這個鉤子可以在以下幾種場景被呼叫:
1)程式正常退出
2)使用System.exit()
3)終端使用Ctrl+C觸發的中斷
4)系統關閉
5)使用Kill pid命令幹掉程序

歡迎掃描二維碼,關注公眾號