JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止

背景

当单线程的程序发生一个未捕获的异常时我们可以采用try....catch进行异常的捕获,但是在多线程环境中,线程抛出的异常是不能用try....catch捕获的,这样就有可能导致一些问题的出现,比如异常的时候无法回收一些系统资源,或者没有关闭当前的连接等等。

package com.exception;

public class NoCaughtThread
{
public static void main(String[] args)
{
try
{
Thread thread = new Thread(new Task());
thread.start();
}
catch (Exception e)
{
System.out.println("==Exception: "+e.getMessage());
}
}
} class Task implements Runnable
{
@Override
public void run()
{
System.out.println(3/2);
System.out.println(3/0);
System.out.println(3/1);
}
}

运行结果:

1
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at com.exception.Task.run(NoCaughtThread.java:25)
at java.lang.Thread.run(Unknown Source)

可以看到在多线程中通过try....catch试图捕获线程的异常是不可取的。

Thread的run方法是不抛出任何检查型异常的,但是它自身却可能因为一个异常而被中止,导致这个线程的终结。
首先介绍一下如何在线程池内部构建一个工作者线程,如果任务抛出了一个未检查异常,那么它将使线程终结,但会首先通知框架该现场已经终结。然后框架可能会用新的线程来代替这个工作线程,也可能不会,因为线程池正在关闭,或者当前已有足够多的线程能满足需要。当编写一个向线程池提交任务的工作者类线程类时,或者调用不可信的外部代码时(例如动态加载的插件),使用这些方法中的某一种可以避免某个编写得糟糕的任务或插件不会影响调用它的整个线程。

主动方法解决

package com.exception;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class InitiativeCaught
{
public void threadDeal(Runnable r, Throwable t)
{
System.out.println("==Exception: "+t.getMessage());
} class InitialtiveThread implements Runnable
{
@Override
public void run()
{
Throwable thrown = null;
try
{
System.out.println(3/2);
System.out.println(3/0);
System.out.println(3/1);
}
catch(Throwable e)
{
thrown =e;
}
finally{
threadDeal(this,thrown);
}
}
} public static void main(String[] args)
{
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new InitiativeCaught().new InitialtiveThread());
exec.shutdown();
} }

运行结果:

1
==Exception: / by zero

Thread API 解决

上面介绍了一种主动方法来解决未检测异常。在Thread ApI中同样提供了UncaughtExceptionHandle,它能检测出某个由于未捕获的异常而终结的情况。这两种方法是互补的,通过将二者结合在一起,就能有效地防止线程泄露问题。

package com.exception;

import java.lang.Thread.UncaughtExceptionHandler;

public class WitchCaughtThread
{
public static void main(String args[])
{
Thread thread = new Thread(new Task());
thread.setUncaughtExceptionHandler(new ExceptionHandler());
thread.start();
}
} class ExceptionHandler implements UncaughtExceptionHandler
{
@Override
public void uncaughtException(Thread t, Throwable e)
{
System.out.println("==Exception: "+e.getMessage());
}
}

运行结果:

1
==Exception: / by zero

同样可以为所有的Thread设置一个默认的UncaughtExceptionHandler,通过调用Thread.setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)方法,这是Thread的一个static方法。

package com.exception;

import java.lang.Thread.UncaughtExceptionHandler;

public class WitchCaughtThread
{
public static void main(String args[])
{
Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());
Thread thread = new Thread(new Task());
thread.start();
}
} class ExceptionHandler implements UncaughtExceptionHandler
{
@Override
public void uncaughtException(Thread t, Throwable e)
{
System.out.println("==Exception: "+e.getMessage());
}
}

运行结果:

1
==Exception: / by zero

线程池的异常捕获

execute方法提交任务

这样做不能捕获到异常:

public class ExecuteCaught
{
public static void main(String[] args)
{
ExecutorService exec = Executors.newCachedThreadPool();
Thread thread = new Thread(new Task());
thread.setUncaughtExceptionHandler(new ExceptionHandler());
exec.execute(thread);
exec.shutdown();
}
}

运行结果:

1
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
at com.exception.Task.run(NoCaughtThread.java:25)
at java.lang.Thread.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

这时需要将异常的捕获封装到Runnable或者Callable中,如下所示:

package com.exception;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ExecuteCaught
{
public static void main(String[] args)
{
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ThreadPoolTask());
exec.shutdown();
}
} class ThreadPoolTask implements Runnable
{
@Override
public void run()
{
Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler());
System.out.println(3/2);
System.out.println(3/0);
System.out.println(3/1);
}
}

运行结果:

1
==Exception: / by zero

submit方法提交任务

只有通过execute提交的任务,才能将它抛出的异常交给UncaughtExceptionHandler,而通过submit提交的任务,无论是抛出的未检测异常还是已检查异常,都将被认为是任务返回状态的一部分。如果一个由submit提交的任务由于抛出了异常而结束,那么这个异常将被Future.get封装在ExecutionException中重新抛出。

package com.exception;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class SubmitCaught
{
public static void main(String[] args)
{
ExecutorService exec = Executors.newCachedThreadPool();
exec.submit(new Task());
exec.shutdown();
}
}
package com.exception;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class SubmitCaught
{
public static void main(String[] args)
{
ExecutorService exec = Executors.newCachedThreadPool();
exec.submit(new ThreadPoolTask());
exec.shutdown();
}
}

上面两段代码运行结果都是:

1

通过这个例子可以看到捕获的异常:

package com.exception;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class SubmitCaught
{
public static void main(String[] args)
{
ExecutorService exec = Executors.newCachedThreadPool();
Future<?> future = exec.submit(new Task());
exec.shutdown();
try
{
future.get();
}
catch (InterruptedException | ExecutionException e)
{
System.out.println("==Exception: "+e.getMessage());
}
}
}

运行结果:

1
==Exception: java.lang.ArithmeticException: / by zero

JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止的更多相关文章

  1. java多线程之:创建开启一个线程的开销

    ---->关于时间,创建线程使用是直接向系统申请资源的,这里调用系统函数进行分配资源的话耗时不好说.---->关于资源,Java线程的线程栈所占用的内存是在Java堆外的,所以是不受jav ...

  2. Java多线程之ConcurrentSkipListMap深入分析(转)

    Java多线程之ConcurrentSkipListMap深入分析   一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...

  3. JAVA多线程之wait/notify

    本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...

  4. JAVA多线程之volatile 与 synchronized 的比较

    一,volatile关键字的可见性 要想理解volatile关键字,得先了解下JAVA的内存模型,Java内存模型的抽象示意图如下: 从图中可以看出: ①每个线程都有一个自己的本地内存空间--线程栈空 ...

  5. java多线程之yield,join,wait,sleep的区别

    Java多线程之yield,join,wait,sleep的区别 Java多线程中,经常会遇到yield,join,wait和sleep方法.容易混淆他们的功能及作用.自己仔细研究了下,他们主要的区别 ...

  6. Java多线程之Runnable与Thread

    Java多线程之Thread与Runnable 一.Thread VS Runnable 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类和 ...

  7. java多线程之wait和notify协作,生产者和消费者

    这篇直接贴代码了 package cn.javaBase.study_thread1; class Source { public static int num = 0; //假设这是馒头的数量 } ...

  8. Java——多线程之Lock锁

    Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线 ...

  9. Java多线程之ReentrantLock重入锁简介与使用教程

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6543947.html  我们知道,线程安全问题需要通过线程之间的同步来解决,而同步大多使用syncrhoize ...

随机推荐

  1. osg创建灯光

    添加光照 osg::ref_ptr<osg::Node> MyOSGLoadEarth::CreateNode() { osg::ref_ptr<osg::Group> _ro ...

  2. flutter的 图片组件基本使用

    import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends Statele ...

  3. 数据分析入门——pandas之DataFrame基本概念

    一.介绍 数据帧(DataFrame)是二维数据结构,即数据以行和列的表格方式排列. 可以看作是Series的二维拓展,但是df有行列索引:index.column 推荐参考:https://www. ...

  4. 正则表达式入门教程&&经典Javascript正则表达式

    前言 例子: ^.+@.+\\..+$ 这样的代码曾经多次把我自己给吓退过.可能很多人也是被这样的代码给吓跑的吧.继续阅读本文将让你也可以自由应用这样的代码. 正文 教程:正则表达式30分钟入门教程 ...

  5. 【ARTS】01_41_左耳听风-201900819~201900825

    ARTS: Algrothm: leetcode算法题目 Review: 阅读并且点评一篇英文技术文章 Tip/Techni: 学习一个技术技巧 Share: 分享一篇有观点和思考的技术文章 Algo ...

  6. socket编程(二)

    TCP下粘包问题 两种情况下会发生粘包. 1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包) 发送方:AB #其实放在缓存里没发送 发送方:B #其 ...

  7. python开发【学习目录】:目录

    python开发[学习目录]:目录 Python开发:环境搭建(python3.PyCharm) Python开发[第一篇]:初识Python Python开发[第二篇]:Python基础知识 Pyt ...

  8. [转帖]04-创建kubeconfig认证文件

    04-创建kubeconfig认证文件 https://www.cnblogs.com/guigujun/p/8366530.html 学习一下 貌似挺有用的. 本文档记录自己的学习历程! 创建 ku ...

  9. 在CentOS7上安装OpenJDK1.8 & OracleJDK1.8

    安装OpenJDK1.8 : 1.检查当前机器是否有自带的JDK rpm -qa |grep java rpm -qa |grep jdk rpm -qa |grep gcj 2.如果没有 则跳至安装 ...

  10. (一)linux 学习 -- 在文件系统中跳转

    The Linux Command Line 读书笔记 - 部分内容来自 http://billie66.github.io/TLCL/book/chap03.html 文章目录 文件系统 根目录 打 ...