0
点赞
收藏
分享

微信扫一扫

Java多线程--全局异常处理--方法/实例


简介

说明

        本文用示例介绍如何全局捕获处理Java多线程中的异常。

        在Java中,线程中的异常是不能抛出到调用该线程的外部方法中捕获的,只能在线程内部进行捕获。

为什么不能抛出异常到外部线程捕获?

        因为线程是独立执行的代码片断,线程的问题应该由线程自己来解决,而不要委托到外部。基于这样的设计理念,在Java中,线程方法的异常都应该在线程代码边界之内(run方法内)进行处理。换句话说,我们不能捕获从线程中逃逸的异常。

JDK如何控制线程异常不会跑到外部线程?

通过java.lang.Runnable.run()方法声明(因为此方法声明上没有throw exception部分)进行了约束。

如果在线程中抛出异常线程会怎么样?

线程会立即终结。

如何捕获线程中的异常?

法1:使用try-catch-finally来包含代码块

法2:使用全局异常处理 UncaughtExceptionHandler

        UncaughtExceptionHandler能检测出某个线程由于未捕获的异常而终结的情况。当一个线程由于未捕获异常而退出时,JVM会把这个事件报告给应用程序提供的UncaughtExceptionHandler异常处理器(这是Thread类中的接口):

public interface UncaughtExceptionHanlder {
void uncaughtException(Thread t, Throwable e);
}

        JDK5之后可以在每一个Thread对象上添加一个异常处理器UncaughtExceptionHandler 。Thread.UncaughtExceptionHandler.uncaughtException()方法会在线程因未捕获的异常而面临死亡时被调用。

实例:不捕获异常

package com.example.a;

class MyThread extends Thread {
private String name;

public MyThread(String name) {
this.name = name;
}

@Override
public void run() {
System.out.println(name + ":运行开始");
int i = 1 / 0;
System.out.println(name + ":运行结束");
}
}

public class Demo {
public static void main(String[] args) {
System.out.println("主线程开始");
Thread thread = new Thread(new MyThread("线程1"));
thread.start();
System.out.println("主线程结束");
}
}

执行结果

主线程开始
主线程结束
线程1:运行开始
Exception in thread "Thread-1" java.lang.ArithmeticException: / by zero
at com.example.a.MyThread.run(Demo.java:13)
at java.lang.Thread.run(Thread.java:748)

全局异常处理

        本处只展示全局异常处理的方案。在线程代码里边加try catch这种方法就不贴出了,很基础。

        法1,2,3也同样适用于Runnable方式创建的线程,比如:Thread thread = new Thread(new MyRunnable("线程1"));

法1:handler

package com.example.a;

class MyThread extends Thread {
private String name;

public MyThread(String name) {
this.name = name;
}

@Override
public void run() {
System.out.println(name + ":运行开始");
int i = 1 / 0;
System.out.println(name + ":运行结束");
}
}

class MyUncheckedExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕获到异常。异常栈信息为:");
e.printStackTrace();
}
}

public class Demo {
public static void main(String[] args) {
System.out.println("主线程开始");
Thread thread = new Thread(new MyThread("线程1"));
thread.setUncaughtExceptionHandler(new MyUncheckedExceptionHandler());
thread.start();
System.out.println("主线程结束");
}
}

执行结果

主线程开始
主线程结束
线程1:运行开始
捕获到异常。异常栈信息为:
java.lang.ArithmeticException: / by zero
at com.example.a.MyThread.run(Demo.java:13)
at java.lang.Thread.run(Thread.java:748)

法2:线程组

package com.example.a;

public class Demo {
public static void main(String[] args) {
System.out.println("主线程开始");

ThreadGroup threadGroup = new ThreadGroup("MyGroup") {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕获到异常。异常栈信息为:");
e.printStackTrace();
}
};

Thread thread = new Thread(threadGroup, new Runnable() {
@Override
public void run() {
System.out.println("子线程运行开始");
int i = 1 / 0;
System.out.println("子线程运行结束");
}
});

thread.start();

System.out.println("主线程结束");
}
}

运行结果

主线程开始
主线程结束
子线程运行开始
捕获到异常。异常栈信息为:
java.lang.ArithmeticException: / by zero
at com.example.a.Demo$2.run(Demo.java:19)
at java.lang.Thread.run(Thread.java:748)

法3:默认的handler

        如果只需要一个线程异常处理器处理线程的异常,那么我们可以设置一个默认的线程异常处理器,当线程出现异常时,

        如果没有指定线程的异常处理器,而且线程组也没有设置,那么就会使用默认的线程异常处理器

package com.example.a;

class MyThread extends Thread {
private String name;

public MyThread(String name) {
this.name = name;
}

@Override
public void run() {
System.out.println(name + ":运行开始");
int i = 1 / 0;
System.out.println(name + ":运行结束");
}
}

class MyUncheckedExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕获到异常。异常栈信息为:");
e.printStackTrace();
}
}

public class Demo {
public static void main(String[] args) {
System.out.println("主线程开始");
Thread.setDefaultUncaughtExceptionHandler(new MyUncheckedExceptionHandler());
Thread thread = new Thread(new MyThread("线程1"));
thread.start();
System.out.println("主线程结束");
}
}

执行结果

主线程开始
主线程结束
线程1:运行开始
捕获到异常。异常栈信息为:
java.lang.ArithmeticException: / by zero
at com.example.a.MyThread.run(Demo.java:13)
at java.lang.Thread.run(Thread.java:748)

法4:FutureTask

上面说的3种方法都是基于线程异常处理器实现的,本方法不需要线程异常处理器。

package com.example.a;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Demo {
public static void main(String[] args) {
System.out.println("主线程开始");

//1.创建FeatureTask
FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("子线程运行开始");
return 1 / 0;
}
});

//2.创建Thread
Thread thread = new Thread(futureTask);

//3.启动线程
thread.start();

try {
Integer result = futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
//4.处理捕获的线程异常
System.out.println("捕获到异常。异常栈信息为:");
e.printStackTrace();
}

System.out.println("主线程结束");

}
}

执行结果

主线程开始
子线程运行开始
捕获到异常。异常栈信息为:
主线程结束
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.example.a.Demo.main(Demo.java:27)
Caused by: java.lang.ArithmeticException: / by zero
at com.example.a.Demo$1.call(Demo.java:16)
at com.example.a.Demo$1.call(Demo.java:12)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.lang.Thread.run(Thread.java:748)


举报

相关推荐

0 条评论