1. 程式人生 > >如何捕獲java執行緒中的逃逸的異常

如何捕獲java執行緒中的逃逸的異常

更多的相關的文章:http://www.cnblogs.com/lao-liang/p/5056176.html;(解釋了java中異常的一些分類);http://www.cnblogs.com/freeliver54/archive/2011/10/17/2215423.html;(解釋瞭如何捕獲執行緒中出現的異常)

前言:由於執行緒中的本質特性,使得你不能捕獲從執行緒中逃逸的異常。一旦異常逃出任務的run()方法,它就會向外傳播,它就會向外傳播到控制檯,除非你採取特殊的步驟來捕獲這種錯誤的異常。在javaSE5之前,你可以使用執行緒組來捕獲這些異常。但是有了java SE5,就可以使用Executor來解決這個問題。因而你不需要了解有關執行緒組的任何的只是了。

   例如在下面的例子中,就會由於在run方法中,丟擲了異常,卻無法捕獲,從而導致了執行緒的停止執行。

總的來說,在java執行緒中,在run方法中,我們要在run()方法中,把一切的異常有處理掉,也就try-catch掉。不能讓這個執行緒丟擲異常,因為如果我們不使用特殊的方式的話,我們是無法捕獲從這個執行緒中逃逸的異常的。異常一旦丟擲了,那麼這個執行緒就會停止執行,但是不會影響主執行緒和其它的執行緒。因為主執行緒和其它的執行緒都不知道它丟擲了異常。
public class ExceptionThread implements  Runnable {
    @Override
public void 
run() { throw new RuntimeException(); } public static void main(String[] args) { Thread thread = new Thread(new ExceptionThread()); ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(thread); } }
執行的結果為:
Exception in thread "pool-1-thread-1" java.lang.RuntimeException
at Concurrency.ExceptionThread.run(ExceptionThread.java:9)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
即使將main的主體放到try-catch語句塊中也是沒有用的。一樣會出現未捕獲的異常。
public static void main(String[] args) {
    try {
        Thread thread = new Thread(new ExceptionThread());
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(thread);
} catch (Exception e) {
        e.printStackTrace();
}
}
為了解決這個問題,我們要修改Executor中產生執行緒的方式。Thread.UnCaughtExecptionHandler是javaSE5中的新介面,它允許你為每一個Thread物件都附上一個異常處理器。比如像以下的這個例子:
class ExceptionThread2 implements  Runnable{
    @Override
public void run() {
        Thread t= Thread.currentThread();
System.out.println("run() by" + t);
System.out.println("eh=" + t.getUncaughtExceptionHandler());
        throw new RuntimeException();
}
}

class MyUncaughtExceptionHandler implements  Thread.UncaughtExceptionHandler{
    @Override
public void uncaughtException(Thread thread, Throwable ex) {
        System.out.println("caught" + ex);
}
}
//呼叫工廠化方法為每一個thread物件都附上一個未捕獲異常的處理器。
class HandlerThreadFactory implements ThreadFactory{
    @Override
public Thread newThread(Runnable r) {
        System.out.println(this + "creating new Thread");
Thread t = new Thread(r);
System.out.println("created " + t);
t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
System.out.println("eh=" + t.getUncaughtExceptionHandler());
        return t;
}
}
public class CaptureUncaughtException {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
exec.execute(new ExceptionThread2());
}
}
執行的結果為:
[email protected] creating new Thread
created Thread[Thread-0,5,main]
[email protected]
run() byThread[Thread-0,5,main]
[email protected]
[email protected] creating new Thread
created Thread[Thread-1,5,main]
[email protected]
caughtjava.lang.RuntimeException
上面的示例使得你可以按照具體的情況逐個地設定處理器。如果你知道將要在程式碼中處處使用相同的異常處理器,那麼更加簡單的方式是在Thread類中設定一個靜態域,並將這個處理器設定為預設的未捕獲異常的處理器。比如像下面的例子:
public class SettingDefaultHandler{
 public static void main(String[] args){
   Thread.setDefaultUncaughtException(new MyUncaughtExceptionHandler());
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
 }
}
說明:這個處理器只有在不存線上程專有的未捕獲異常的情況下才會被呼叫。系統會檢查執行緒的專有的版本,如果沒有發現,則檢查執行緒組是有有其專有的uncaughtException()方法。如果也沒有,在呼叫defaultUncaughtExcept

   在java多執行緒程式中,所有執行緒都不允許丟擲未捕獲的checked exception(比如sleep時的InterruptedException),也就是說各個執行緒需要自己把自己的checked exception處理掉。這一點是通過java.lang.Runnable.run()方法宣告(因為此方法宣告上沒有throw exception部分)進行了約束。但是執行緒依然有可能丟擲unchecked exception(如執行時異常),當此類異常跑丟擲時,執行緒就會終結,而對於主執行緒和其他執行緒完全不受影響,且完全感知不到某個執行緒丟擲的異常(也是說完全無法catch到這個異常)。JVM的這種設計源自於這樣一種理念:“執行緒是獨立執行的程式碼片斷,執行緒的問題應該由執行緒自己來解決,而不要委託到外部。”基於這樣的設計理念,在Java中,執行緒方法的異常(無論是checked還是unchecked exception),都應該線上程程式碼邊界之內(run方法內)進行try catch並處理掉.換句話說,我們不能捕獲從執行緒中逃逸的異常。
看下面的例子:

複製程式碼
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExceptionThread implements Runnable {

    @Override
    public void run() {
        throw new RuntimeException("這個執行緒就幹了這麼一件事,丟擲一個執行時異常");
    }
    
    public static void main(String[] args) {
        try {
            ExecutorService exec = Executors.newCachedThreadPool();
            exec.execute(new ExceptionThread());
            System.out.println("該幹嘛幹嘛去");
        } catch (RuntimeException e) {
            System.out.println("能不能捕獲到異常?");
        }
        
    }

}
複製程式碼

執行結果:

該幹嘛幹嘛去
Exception in thread "pool-1-thread-1" java.lang.RuntimeException: 這個執行緒就幹了這麼一件事,丟擲一個執行時異常
    at ExceptionThread.run(ExceptionThread.java:8)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:619)

從執行結果中,我們可以看到的是,這個異常在main執行緒中沒有catch到,即

  System.out.println("能不能捕獲到異常?");

   永遠不會執行到。

        問題來了,我們如果需要捕獲其執行緒的unchecked異常時該怎麼辦?Java SE5之後,我們可以通過Executor來解決這個我問題。為了解決這個問題,我們需要修改Executor產生執行緒的方式。Thread.UncaughtExceptionHandler是java SE5中的新介面,它允許我們在每一個Thread物件上新增一個異常處理器。(UncaughtExceptionHandler)。Thread.UncaughtExceptionHandler.uncaughtException()方法會線上程因未捕獲的異常而面臨死亡時被呼叫。下面這個例子簡單的演示瞭如何使用UncaughtExceptionHandler

public class ExceptionThread2 implements Runnable {@Overridepublic void run() {Thread t = Thread.currentThread();System.out.println("run() by" + t);System.out.println("eh=" + t.getUncaughtExceptionHandler());throw new RuntimeException("丟擲執行時異常");}}
複製程式碼
import java.lang.Thread.UncaughtExceptionHandler;

/**
 * 用於捕獲異常---捕獲的是uncheckedException
 * 
 * @author February30th
 * 
 */
public class MyUnchecckedExceptionhandler implements UncaughtExceptionHandler {

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("捕獲到異常:" + e);
    }

}
複製程式碼複製程式碼
import java.util.concurrent.ThreadFactory;

public class HandlerThreadFactory implements ThreadFactory {

    @Override
    public Thread newThread(Runnable r) {
        System.out.println("建立一個新的執行緒");
        Thread t = new Thread(r);
        t.setUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
        System.out.println("eh121 = " + t.getUncaughtExceptionHandler());
        return t;
    }

}
複製程式碼複製程式碼
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ThreadExceptionTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        //下面有3中方式來執行執行緒。
        //第1種按照普通的方式。這是能捕獲到異常
        Thread t = new Thread(new ExceptionThread2());
        t.setUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
        t.start();
        //第2種按照現成池,直接按照thread方式,此時不能捕獲到異常,為什麼呢?因為在下面程式碼中建立了一個執行緒,且設定了異常處理器,
        //但是呢,在我們執行緒池中會重設定新的Thread物件,而這個Thread物件沒有設定任何異常處理器,換句話說,我們線上程池外對執行緒做的
        //任何操作都是沒有用的
        ExecutorService exec1 = Executors.newCachedThreadPool();
        Runnable runnable = new ExceptionThread2();
        Thread t1 = new Thread(runnable);
        t1.setUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
        exec1.execute(runnable);
        
        //第3種情況一樣的,也是走的執行緒池,但是呢是通過ThreadFactory方式,在ThreadFactory中會對執行緒做一些控制,可以設定異常處理器
        //此時是可以捕獲異常的。
        ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
        exec.execute(new ExceptionThread2());
        
    }

}
複製程式碼

執行結果

建立一個新的執行緒
eh121 = [email protected]
run() byThread[Thread-0,5,main]
eh=[email protected]
捕獲到異常:java.lang.RuntimeException: 丟擲執行時異常

從上述的執行結果中可以看到,未捕獲的異常是通過uncaughtException來捕獲的。

按照上述的例項,我們可以按照具體的情況,逐個地設定處理器。但是如果我們知道將要在程式碼的所有地方都是用相同的異常處理器,那麼更簡單的方式是在Thread類中設定一個靜態域,並將這個處理器設定為預設的未捕獲異常處理器。看下面的例子:

Thread.setDefaultUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
        ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
        exec.execute(new ExceptionThread2());

這個預設的處理器只有在執行緒不存在非預設的異常處理器時才會呼叫。 在執行時,系統會檢查執行緒是否有屬於自己的異常處理器,如果發現沒有,就去檢查相應的執行緒組是否有專有的異常處理器,如果發現也沒有,再呼叫預設的異常處理器。


相關推薦

如何捕獲java執行逃逸異常

更多的相關的文章:http://www.cnblogs.com/lao-liang/p/5056176.html;(解釋了java中異常的一些分類);http://www.cnblogs.com/freeliver54/archive/2011/10/17/2215423.

Java執行捕獲執行異常

在某些場景下,我們經常需要使用多執行緒來執行任務提高效能,但是我們知道正常的主執行緒是無法處理子執行緒的異常的,一旦出現異常就會傳播到控制檯。這個時候我們需要線上程裡面處理異常怎麼辦呢,我們可以使用E

java捕獲執行異常

Java中在處理異常的時候,通常的做法是使用try-catch-finally來包含程式碼塊,但是Java自身還有一種方式可以處理——使用UncaughtExceptionHandler。它能檢測出某個執行緒由於未捕獲的異常而終結的情況。當一個執行緒由於未捕獲異常而退出時,JVM會把這個事件報告

java執行異常處理

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

執行異常捕獲

根據java執行緒的本質,當一個執行緒丟擲異常時,在主執行緒中加try catch 是無法捕獲到其丟擲的異常的,如下面程式碼所示: private static final String TAG = "MainActivity"; @Overri

java 執行的 wait()和sleep()

wait() 方法是寫在java.lang.Object類中的 (ps: notify()  notifyAll()也是在Object中定義的) wait()原始碼註釋: Causes the current thread to wait until either a

Java執行阻塞佇列

Java語言提供了java.util.concurrent包解決執行緒同步問題,concurrent包中的阻塞佇列BlockingQueue能夠很好地執行緒同步問題, 介面BlockingQueue提供如下幾個執行緒同步方法 儲存資料:   offer(obj):向佇列BlockingQue

執行複習筆記之四【多執行異常

在一個執行緒組中如果其中一個執行緒執行報錯並不影響其他執行緒的繼續執行,例如: package com.fyw.thread.lock.exception; public class MyThread extends Thread { private String num; publi

Java執行的問題——競態條件、資料競爭

1.競態條件 說得通俗一點,就是執行緒A 需要判斷一個變數的狀態,然後根據這個變數的狀態來執行某個操作。在執行這個操作之前,這個變數的狀態可能會被其他執行緒修改。 典型情況: (1)check-then-act if(a == 10.0) { b = a /

深度解析Java執行池的異常處理機制

前言 今天小夥伴遇到個小問題,執行緒池提交的任務如果沒有catch異常,那麼會拋到哪裡去,之前倒是沒研究過,本著實事求是的原則,看了一下程式碼。 正文 小問題 考慮下面這段程式碼,有什麼區別呢?你可以猜猜會不會有異常打出呢?如果打出來的話是在哪裡?: ExecutorSe

Java執行yield與join方法的區別

長期以來,多執行緒問題頗為受到面試官的青睞。雖然我個人認為我們當中很少有人能真正獲得機會開發複雜的多執行緒應用(在過去的七年中,我得到了一個機會),但是理解多執行緒對增加你的信心很有用。之前,我討論了一個wait()和sleep()方法區別的問題,這一次,我將會討

java執行加入執行

加入執行緒  join()                join(int)  等待指定毫秒之後再繼續。理解 :暫停當前執行的執行緒,開始執行當前加入的執行緒,完畢後繼續執行暫停的執行緒。

執行異常處理

前2種方法都是在子執行緒中處理,第3種方法是在父執行緒中處理。 具體用哪一種方法,取決於這個異常是否適合在子執行緒中處理。例如有些異常更適合由呼叫方(父執行緒)處理,那麼此時就應當用第3種方法。 方法一:子執行緒中try... catch... 最簡單有效的辦法,就是在子執行緒的執行方法中,把

JAVA執行的關於wait和notify不錯的例子

JAVA執行緒中的關於wait和notify不錯的例子,原來的實現方法:   public class ListAdd1 { private volatile static List list = new ArrayList(); public void add(){

java執行yield()和join()的區別

package test.core.threads; public class JoinExample { public static void main(String[] args) throws InterruptedException { Thread t = new Thr

java執行yield(),sleep(),wait()區別詳解

1、sleep() 使當前執行緒(即呼叫該方法的執行緒)暫停執行一段時間,讓其他執行緒有機會繼續執行,但它並不釋放物件鎖。也就是說如果有synchronized同步快,其他執行緒仍然不能訪問共享資料。

java執行Callble和Future的使用

     Callable是java.util.concurrent包中一個介面,Callable 介面類似於Runnable,兩者都是為那些其例項可能被另一個執行緒執行的類設計的。但是Runnable 不會返回結果,並且無法丟擲經過檢查的異常。此介面中就聲明瞭一個方法c

java挑戰高併發(7):java 執行yield的用法

Thread.yield()方法作用是:暫停當前正在執行的執行緒物件,並執行其他執行緒。 yield()應該做的是讓當前執行執行緒回到可執行狀態,以允許具有相同優先順序的其他執行緒獲得執行機會。因此,使用yield()的目的是讓相同優先順序的執行緒之間能適當的輪轉執行。但

Java執行sleep()、wait()和notify()和notifyAll()、yield()、join()等方法的用法和區別

Java執行緒中sleep()、wait()和notify()和notifyAll()、suspend和resume()、yield()、join()、interrupt()的用法和區別從作業系統的角度講,os會維護一個ready queue(就緒的執行緒佇列)。並且在某一

java執行Exchanger使用

有時我們需要對元素進行配對和交換執行緒的同步點,使用exchange方法 返回其夥伴的物件,這時我們就需要使用執行緒類中的Exchanger類了, 我通過一個例項 來簡單說明一下他的使用方法及其作用: import java.util.concurrent.Exchange