1. 程式人生 > >多執行緒的異常處理

多執行緒的異常處理

本文摘自:Java併發程式設計從入門到精通(張振華     著)

在併發程式設計中,run( )方法不允許thorw exception,所有的異常必須在run方法內進行處理。

在java多執行緒程式中,所有的執行緒都不允許丟擲未捕獲的checked exception,也就是說各個執行緒需要自己把自己的checked exception處理掉。這一點是通過java.lang.Runnable.run( )方法宣告(因為此方法宣告上沒有throw exception部分)進行了約束。但是執行緒依然有可能丟擲unchecked exception,當丟擲此類異常時,執行緒就會終結,而對於主執行緒和其他執行緒完全不受影響,且完全感知不到某個執行緒丟擲的異常,也就是說完全無法catch到這個異常。

因此,在thread裡面,如果要處理checked exception,簡單的一個try/catch塊就可以了。對於這種unchecked exception,相對來說就會有點不一樣。

這是就要用到Thread裡面的setUncaughtExceptionHandler(UncaughtExceptionHandler),這個方法可以用來處理一些unchecked exception。setUncaughtExceptionHandler( )方法相當於一事件註冊的入口。在Thread類裡面的原始碼如下:

<span style="font-size:14px;">public void setUncaughtExceptionHandler(UncaughtExceptionHandler paramUncaughtExceptionHandler)
{
 	checkAccess();
 	this.uncaughtExceptionHandler = paramUncaughtExceptionHandler;
}
</span>
<span style="font-family:Courier New;font-size:14px;background-color: rgb(240, 240, 240);">而UncaughtExceptionHandler則是一個介面;它的宣告如下:</span>
<span style="font-size:14px;">public static abstract interface UncaughtExceptionHandler
{
	public anstract void uncaughtException(Thread paramThread, Throwable paramThrowable);
}
</span>
<span style="font-size:14px;">在異常發生時,我們傳入的 UncaughtExceptionHandler引數的uncaughtException方法會被呼叫。</span>
<span style="font-size:14px;">綜合前面的討論,我們把要實現handle unchecked exception的方法的具體步驟總結如下:</span>
<span style="font-size:14px;">(1)定義一個類實現UncaughtExceptionHandler介面。在實現的方法裡包含對異常處理的邏輯和步驟。</span>
<span style="font-size:14px;">(2)定義執行緒執行結構和邏輯。這一步和普通執行緒定義一樣。</span>
<span style="font-size:14px;">(3)在建立和執行該子執行緒的方法中,在thread.start( )語句前增加一個thread.setUncaughtExceptionHandler語句來實現處理邏輯的註冊</span>
<span style="font-size:14px;">
</span>
<span style="font-size:14px;">下面按照這個步驟來建立一個示例:</span>
<span style="font-size:14px;">首先是實現UncaughtExceptionHandler介面部分
</span>
<span style="font-size:14px;">package demo.thread;

import java.lang.Thread.UncaughtExceptionHandler;

public class ExceptionHandlerThreadB implements UncaughtExceptionHandler {
		public void uncaughtException(Thread t, Throwable e) {
			System.out.printf("An exception has been captured\n");
			System.out.printf("Thread: %s\n", t.getId());
			System.out.printf("Exception: %s: %s\n", 
					e.getClass().getName(), e.getMessage());
			System.out.printf("Stack Trace: \n");
			e.printStackTrace(System.out);
			System.out.printf("Thread status: %s\n", t.getState());
		}
	}
</span>

<span style="font-size:14px;">這裡我們新增的異常處理邏輯很簡單,只是把執行緒的資訊和異常資訊都打印出來。</span>
<span style="font-size:14px;">然後,我們定義執行緒的內容,這裡,我們故意讓執行緒產生一個unchecked exception:</span>
<span style="font-size:14px;">package demo.thread;

public class ThreadB implements Runnable {
	public void run() {
		int number0 = Integer.parseInt("TTT");
	}
}
</span>

<span style="font-size:14px;">從上面的程式碼中我們可以看到,Integer.parseInt()裡面的引數是錯誤的,肯定會丟擲一個異常來。</span>
<span style="font-size:14px;">現在,我們再把建立執行緒和註冊處理邏輯的部分補上來:</span>
<span style="font-size:14px;">package demo.thread;
public class ThreadMain {
	public static void main(String[] args) {
		ThreadB task = new ThreadB();
		Thread thread = new Thread(task);
		thread.setUncaughtExceptionHandler(new ExceptionHandlerThreadB());
		thread.start();
	}
}</span>

<span style="font-size:14px;">現在我們執行整個程式,會發現有如下的結果
</span>
<span style="font-size:14px;">An exception has been captured
Thread: 10
Exception: java.lang.NumberFormatException: For input string: "TTT"
Stack Trace: 
java.lang.NumberFormatException: For input string: "TTT"
	at java.lang.NumberFormatException.forInputString(Unknown Source)
	at java.lang.Integer.parseInt(Unknown Source)
	at java.lang.Integer.parseInt(Unknown Source)
	at unit2.ThreadB1.run(ThreadB1.java:5)
	at java.lang.Thread.run(Unknown Source)
Thread status: RUNNABLE
</span>

這部分的輸出正好就是我們前面實現UncaughtExceptionHandler介面的定義。

因此,對於unchecked exception,我們也可以採用類似事件註冊的機制做一定程度的處理,當然也可以不做處理放任自由,讀者可以試試效果什麼怎麼樣的,這裡不再實驗。

總之,Java thread裡面關於異常的部分比較奇特。你不能直接在一個執行緒裡面去丟擲異常。一般線上程裡碰到checked exception,推薦的做法是採用try/catch塊來處理。而對於unchecked exception,比較合理的方式是註冊一個實現UncaughtExceptionHandler介面的物件例項來處理。

<span style="font-size:14px;">
</span>