如何捕獲java線程中的逃逸的異常

UrsulaHargr 7年前發布 | 16K 次閱讀 線程 Java Java開發

摘要: 在java線程中,在run方法中,我們要在run()方法中,把一切的異常有處理掉,也就try-catch掉。不能讓這個線程拋出異常,因為如果我們不使用特殊的方式的話,我們是無法捕獲從這個線程中逃逸的異常的。異常一旦拋出了,那么這個線程就會停止運行,但是不會影響主線程和其它的線程。因為主線程和其它的線程都不知道它拋出了異常。 線程在run方法拋出異常,沒有catch

在java線程中,在run方法中,我們要在run()方法中,把一切的異常有處理掉,也就try-catch掉。不能讓這個線程拋出異常,因為如果我們不使用特殊的方式的話,我們是無法捕獲從這個線程中逃逸的異常的。異常一旦拋出了,那么這個線程就會停止運行,但是不會影響主線程和其它的線程。因為主線程和其它的線程都不知道它拋出了異常。

線程在run方法拋出異常,沒有catch

那么會有疑問,是不是在main函數里面沒有catch

``
package Thread.UncaughtExceptionHandler;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 測試線程拋異常不能捕獲
 * @author yxx
 */
public class ExceptionThread implements Runnable {

    @Override
    public void run() {
        System.out.println("我準備拋出異常,你能接到嗎?");
        throw new RuntimeException();
    }
    public static void main(String[] args) {
        try {
            Thread thread = new Thread(new ExceptionThread());
            ExecutorService exec = Executors.newCachedThreadPool();
            exec.execute(thread);
        } catch (Exception e) {
            System.out.println("我要捉住異常");
            e.printStackTrace();
        }
    }
}

一樣是catch不到的,想想一個線程的拋出異常之后就終止了如果再進行catch,都不在一個線程里面

所以這個時候我們得去實現UncaughtExceptionHandler這個接口來捕獲拋出的異常

實現UncaughtExceptionHandler這個接口來捕獲拋出的異常

ExceptionThread2.java

package Thread.UncaughtExceptionHandler;

/**
 * 一個拋出異常的線程類 但是想被抓住
 * @author yxx
 */
public class ExceptionThread2 implements Runnable {

    @Override
    public void run() {
        Thread t = Thread.currentThread();
        System.out.println("run by " + t.getName());
        System.out.println("Exception " + t.getUncaughtExceptionHandler());
        throw new RuntimeException();
    }
}

ExceptionThread3.java

package Thread.UncaughtExceptionHandler;

/**
 * 一個拋出異常的線程類 但是想被抓住
 * @author yxx
 */
public class ExceptionThread3 implements Runnable {

    @Override
    public void run() {
        Thread t = Thread.currentThread();
        System.out.println("run by " + t.getName());
        System.out.println("Exception " + t.getUncaughtExceptionHandler());
        throw new NullPointerException();
    }

}

MyUncaughtExceptionHandler.java

package Thread.UncaughtExceptionHandler;

import java.lang.Thread.UncaughtExceptionHandler;

/**
 * 實現UncaughtExceptionHandler接口,抓住拋出的異常
 * @author yxx
 *
 */
public class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("caugth: " + e.toString());
    }
}

HandlerThreadFactory.java

package Thread.UncaughtExceptionHandler;

import java.util.concurrent.ThreadFactory;

/**
 * 實現ThreadFactory來創建線程,為每一個thread對象都附上一個未捕獲異常的處理器
 * @author yxx
 *
 */
public 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;
    }
}

CaptureUncaughtException.java

package Thread.UncaughtExceptionHandler;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 準備創建線程拋出異常,線程外捕獲
 * @author yxx
 *
 */
public class CaptureUncaughtException {

    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool(new HandlerThreadFactory());
        es.execute(new ExceptionThread2());
        es.execute(new ExceptionThread3());
    }
}

運行截圖

都能捕獲到

如果你知道將要在代碼中處處使用相同的異常處理器,那么更加簡單的方式是在Thread類中設置一個靜態域,并將這個處理器設置為默認的未捕獲異常的處理器。

在Thread類中設置一個靜態域

SettingDefaultHandler.java

package Thread.UncaughtExceptionHandler;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 如果你知道將要在代碼中處處使用相同的異常處理器,那么更加簡單的方式是在Thread類中設置一個靜態域,
 * 并將這個處理器設置為默認的未捕獲異常的處理器。
 * @author yxx
 *
 */
public class SettingDefaultHandler {
    public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());

        ExecutorService es = Executors.newCachedThreadPool();
        es.execute(new ExceptionThread2());
    }
}

靜態域執行效果圖

說明:這個處理器只有在不存在線程專有的未捕獲異常的情況下才會被調用。系統會檢查線程的專有的版本,如果沒有發現,則檢查線程組是有有其專有的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并處理掉.換句話說,我們不能捕獲從線程中逃逸的異常。

 

來自:https://yq.aliyun.com/articles/71330?utm_campaign=wenzhang&utm_medium=article&utm_source=QQ-qun&2017329&utm_content=m_15240

 

 本文由用戶 UrsulaHargr 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!