Java線程系列——線程異常處理

1.子線程中處理異常的弊端

多線程拋出的異常,容易在主線程大量的日志打印中丟失,給日志排查帶來(lái)了一定的困難。如下面的例子:

public class ExceptionInChildThread implements Runnable {
    public static void main(String[] args) {
        new Thread(new ExceptionInChildThread()).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println(i);
        }
    }

    @Override
    public void run() {
        throw new RuntimeException();
    }
}

運(yùn)行結(jié)果:

Exception in thread "Thread-0" java.lang.RuntimeException
0
at uncaughtexception.ExceptionInChildThread.run(ExceptionInChildThread.java:19)
1
at java.lang.Thread.run(Thread.java:745)
2

可見(jiàn),主線程可以輕松發(fā)現(xiàn)異常,子線程卻不行
子線程異常無(wú)法用傳統(tǒng)方法捕獲
在外層加上try/catch是否可行呢?

public class CantCatchDirectly implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        try {
            new Thread(new CantCatchDirectly(), "MyThread-1").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(), "MyThread-2").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(), "MyThread-3").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(), "MyThread-4").start();
            Thread.sleep(300);
        }catch (RuntimeException e){
            System.out.println("Caught Exception.");
        }
    }

    @Override
    public void run() {
        throw new RuntimeException();
    }
}

運(yùn)行結(jié)果:

Exception in thread "MyThread-1" java.lang.RuntimeException
at uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:30)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "MyThread-2" java.lang.RuntimeException
at uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:30)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "MyThread-3" java.lang.RuntimeException
at uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:30)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "MyThread-4" java.lang.RuntimeException
at uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:30)
at java.lang.Thread.run(Thread.java:745)

拋出了4行異常,但根本沒(méi)有打印Caught Exception.可見(jiàn)try/catch失效。
把try/catch放到run方法中是否可行?

public class CantCatchDirectly implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        new Thread(new CantCatchDirectly(), "MyThread-1").start();
        Thread.sleep(300);
        new Thread(new CantCatchDirectly(), "MyThread-2").start();
        Thread.sleep(300);
        new Thread(new CantCatchDirectly(), "MyThread-3").start();
        Thread.sleep(300);
        new Thread(new CantCatchDirectly(), "MyThread-4").start();
        Thread.sleep(300);
    }

    @Override
    public void run() {
        try {
            throw new RuntimeException();
        } catch (RuntimeException e) {
            System.out.println("Caught Exception.");
        }
    }
}

運(yùn)行結(jié)果:

Caught Exception.
Caught Exception.
Caught Exception.
Caught Exception.

可行,但代碼太丑。有沒(méi)有更好的辦法?

2.利用UncaughtExceptionHandler接口實(shí)現(xiàn)

UncaughtExceptionHandler的源碼如下:

public interface UncaughtExceptionHandler {
    /**
     * Method invoked when the given thread terminates due to the
     * given uncaught exception.
     * <p>Any exception thrown by this method will be ignored by the
     * Java Virtual Machine.
     * @param t the thread
     * @param e the exception
     */
    void uncaughtException(Thread t, Throwable e);
}

可以參考一下ThreadGroup異常處理器的調(diào)用策略。
自己實(shí)現(xiàn),可以分三種情況,后兩種屬于精準(zhǔn)定位了:

  • 給程序統(tǒng)一設(shè)置
  • 給每個(gè)線程單獨(dú)設(shè)置
  • 給線程池設(shè)置

代碼如下:

public class MyUncaughtExceptionHandler implements 
Thread.UncaughtExceptionHandler {    private String name;    public MyUncaughtExceptionHandler(String name) {        this.name = name;    }    @Override    public void uncaughtException(Thread t, Throwable e) {        Logger logger = Logger.getAnonymousLogger();        logger.log(Level.WARNING, "線程異常終止了" + t.getName(), e);        System.out.println(name + "捕獲了異常" + t.getName() + "異常" + e);    }}

public class MyUncaughtExceptionHandler implements 
Thread.UncaughtExceptionHandler {
    private String name;
    public MyUncaughtExceptionHandler(String name) {
        this.name = name;
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        Logger logger = Logger.getAnonymousLogger();
        logger.log(Level.WARNING, "線程異常終止了" + t.getName(), e);
        System.out.println(name + "捕獲了異常" + t.getName() + "異常" + e);
    }
}

public class UseOwnUncaughtExceptionHandler implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler("捕獲器1"));
        new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-1").start();
        Thread.sleep(300);
        new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-2").start();
        Thread.sleep(300);
        new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-3").start();
        Thread.sleep(300);
        new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-4").start();
    }

    @Override
    public void run() {
        throw new RuntimeException();
    }
}

運(yùn)行結(jié)果:

捕獲器1捕獲了異常MyThread-1異常java.lang.RuntimeException
Feb 27, 2020 8:09:21 PM uncaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 線程異常終止了MyThread-1
java.lang.RuntimeException
at uncaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:23)
at java.lang.Thread.run(Thread.java:745)
Feb 27, 2020 8:09:21 PM uncaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 線程異常終止了MyThread-2
java.lang.RuntimeException
at uncaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:23)
at java.lang.Thread.run(Thread.java:745)
捕獲器1捕獲了異常MyThread-2異常java.lang.RuntimeException
Feb 27, 2020 8:09:21 PM uncaughtexception.MyUncaughtExceptionHandler uncaughtException
捕獲器1捕獲了異常MyThread-3異常java.lang.RuntimeException
警告: 線程異常終止了MyThread-3
java.lang.RuntimeException
at uncaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:23)
at java.lang.Thread.run(Thread.java:745)
Feb 27, 2020 8:09:22 PM uncaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 線程異常終止了MyThread-4
java.lang.RuntimeException
at uncaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:23)
at java.lang.Thread.run(Thread.java:745)
捕獲器1捕獲了異常MyThread-4異常java.lang.RuntimeException

3. Java異常體系:
Java異常體系.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容