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異常體系:
