1 子线程异常主线程无法感知
- 主线程可以轻松发现异常,子线程却不行
- 子线程异常无法用传统方法捕获
public class NoCaughtThread {
public static void main(String[] args) {
Runnable task = () -> {
System.out.println(3 / 2);
System.out.println(3 / 0);
System.out.println(3 / 1);
};
try {
Thread thread = new Thread(task, "AAA");
thread.start();
} catch (Exception e) { // 抓不到
System.out.println("errMsg=" + e.getMessage());
}
}
}
/**
* 单线程,抛出,处理,有异常堆栈 多线程,子线程发生异常,会有什么不同?
*/
public class ExceptionInChildThread {
public static void main(String[] args) {
new Thread(() -> {
throw new RuntimeException("子线程出错啦...");
}).start();
//依然还在执行
for (int i = 0; i < 6; i++) {
System.out.println(i);
}
}
}
/**
* 1. 不加try catch抛出4个异常,都带线程名字
* 2. 加了try catch,期望捕获到第一个线程的异常,线程234不应该运行,希望看到打印出Caught Exception
* 3. 执行时发现,根本没有Caught Exception,线程234依然运行并且抛出异常
*
* 说明线程的异常不能用传统方法捕获
*/
public class CantCatchDirectly implements Runnable {
@Override
public void run() {
throw new RuntimeException();
}
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();
} catch (RuntimeException e) {
System.out.println("Caught Exception.");
}
}
}
2 如何处理异常
方案一(不推荐):手动在每个run方法里进行 try catch
public class CantCatchDirectly implements Runnable {
@Override
public void run() {
try {
throw new RuntimeException();
} catch (RuntimeException e) {
System.out.println("Caught Exception.");
}
}
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();
}
}
方案二(推荐):利用 UncaughtExceptionHandler
//java.lang.Thread.UncaughtExceptionHandler
@FunctionalInterface
public interface UncaughtExceptionHandler {
void uncaughtException(Thread t, Throwable e);
}
自己实现
- 给程序统一设置
- 给每个线程单独设置
- 给线程池设置
给程序统一设置
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 自己的MyUncaughtExceptionHanlder
*/
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());
System.out.println(name + "捕获了异常" + t.getName() + "异常");
}
}
/**
* 使用刚才自己写的UncaughtExceptionHandler
*/
public class UseOwnUncaughtExceptionHandler {
public static void main(String[] args) throws InterruptedException {
Runnable run = () -> {
throw new RuntimeException();
};
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler("捕获器1"));
new Thread(run, "MyThread-1").start();
Thread.sleep(300);
new Thread(run, "MyThread-2").start();
Thread.sleep(300);
new Thread(run, "MyThread-3").start();
Thread.sleep(300);
new Thread(run, "MyThread-4").start();
}
}
给每个线程单独设置
/**
* 异常处理
*/
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
// 记录日志等
System.out.println("thread=" + t.getName() + ",exception=" + e.getClass().getName() + ",errMsg=" + e.getMessage());
}
}
public class UseOwnUncaughtExceptionHandler {
public static void main(String[] args) {
Runnable run = () -> {
//throw new IllegalArgumentException("参数个数少于预期");
throw new ArithmeticException("分母不能为零");
};
Thread task = new Thread(run, "AAA");
// 局部和全局都设置了,只有局部生效
// 设置多个局部的,后面的会覆盖前面的
task.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
task.start();
}
}
给线程池设置
/**
* 异常处理
*/
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
// 记录日志等
System.out.println("thread=" + t.getName() + ",exception=" + e.getClass().getName() + ",errMsg=" + e.getMessage());
}
}
如果采用线程池通过execute的方法去捕获异常,需要将异常的捕获封装到Runnable或者Callable中
// java.util.concurrent.Executor#execute
void execute(Runnable command);
thread.setUncaughtExceptionHandler 未捕获到异常
// 不能捕获到异常
ExecutorService exec = Executors.newCachedThreadPool();
Thread task = new Thread(() -> {
System.out.println(3 / 2);
System.out.println(3 / 0);
System.out.println(3 / 1);
});
// 无效的设置
task.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
exec.execute(task);
exec.shutdown();
//1
//Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
// at UseOwnUncaughtExceptionHandler.lambda$main$0(UseOwnUncaughtExceptionHandler.java:10)
// at java.base/java.lang.Thread.run(Thread.java:830)
// at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
// at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
// at java.base/java.lang.Thread.run(Thread.java:830)
将异常的捕获封装到Runnable或者Callable中,就能捕获到异常
// 将异常的捕获封装到Runnable或者Callable中,就能捕获到异常
ExecutorService exec = Executors.newCachedThreadPool();
Runnable task = () -> {
Thread.currentThread().setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
System.out.println(3 / 2);
System.out.println(3 / 0);
System.out.println(3 / 1);
};
exec.execute(task);
exec.shutdown();
//1
//thread=pool-1-thread-1,exception=java.lang.ArithmeticException,errMsg=/ by zero
只有通过execute提交的任务,才能将它抛出的异常交给UncaughtExceptionHandler,而通过submit提交的任务,无论是抛出的未检测异常还是已检查异常,都将被认为是任务返回状态的一部分。如果一个由submit提交的任务由于抛出了异常而结束,那么这个异常将被Future.get封装在ExecutionException中重新抛出。
ExecutorService exec = Executors.newCachedThreadPool();
Runnable task = () -> {
System.out.println(3 / 2);
System.out.println(3 / 0);
System.out.println(3 / 1);
};
Future<?> res = exec.submit(task);
exec.shutdown();
// 1
ExecutorService exec = Executors.newCachedThreadPool();
Runnable task = () -> {
Thread.currentThread().setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
System.out.println(3 / 2);
System.out.println(3 / 0);
System.out.println(3 / 1);
};
Future<?> res = exec.submit(task);
exec.shutdown();
//1
future.get() 会抛出异常
ExecutorService exec = Executors.newCachedThreadPool();
Runnable task = () -> {
Thread.currentThread().setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
System.out.println(3 / 2);
System.out.println(3 / 0);
System.out.println(3 / 1);
};
Future<?> future = exec.submit(task);
exec.shutdown();
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
System.out.println("errMsg=" + e.getMessage());
}
//1
//errMsg=java.lang.ArithmeticException: / by zero
https://blog.csdn.net/u013256816/article/details/50417822