1.子线程中处理异常的弊端
多线程抛出的异常,容易在主线程大量的日志打印中丢失,给日志排查带来了一定的困难。如下面的例子:
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();
}
}
运行结果:
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
…
可见,主线程可以轻松发现异常,子线程却不行
子线程异常无法用传统方法捕获
在外层加上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();
}
}
运行结果:
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行异常,但根本没有打印Caught Exception.可见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.");
}
}
}
运行结果:
Caught Exception.
Caught Exception.
Caught Exception.
Caught Exception.
可行,但代码太丑。有没有更好的办法?
2.利用UncaughtExceptionHandler接口实现
UncaughtExceptionHandler的源码如下:
public interface UncaughtExceptionHandler {
/**
* Method invoked when the given thread terminates due to the
* given uncaught exception.
* 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异常处理器的调用策略。
自己实现,可以分三种情况,后两种属于精准定位了:
- 给程序统一设置
- 给每个线程单独设置
- 给线程池设置
代码如下:
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();
}
}
运行结果:
捕获器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