浅说多线程(下)

六、线程的异常处理
默认处理
线程都不允许抛出未捕获的checked exception(比如sleep时的InterruptedException),也就是说各个线程需要自己把自己的checked exception处理掉。
那么未捕获的异常哪里去了呢?
实际上一个异常被抛出后,如果没有被捕获处理,则会一直向上抛。异常一旦被Thread.run() 抛出后,就不能在程序中对异常进行捕获,最终只能由JVM捕获。
那JVM又是怎么处理线程中抛出的异常?
实际上JVM是调用Thread 的 dispatchUncaughtException方法:

    /**
     * 向 handler 分派未捕获的异常。 该方法仅由JVM调用。
     */
    private void dispatchUncaughtException(Throwable e) {
        getUncaughtExceptionHandler().uncaughtException(this, e);
    }

    /**
     * Returns the handler invoked when this thread abruptly terminates
     * due to an uncaught exception. If this thread has not had an
     * uncaught exception handler explicitly set then this thread's
     * <tt>ThreadGroup</tt> object is returned, unless this thread
     * has terminated, in which case <tt>null</tt> is returned.
     * @since 1.5
     * @return the uncaught exception handler for this thread
     */
    public UncaughtExceptionHandler getUncaughtExceptionHandler() {
        return uncaughtExceptionHandler != null ?
            uncaughtExceptionHandler : group;
    }

UncaughtExceptionHandler必须显示的设置,否则默认为null。若为null,则使用线程默认的handler,即该线程所属的ThreadGroup。ThreadGroup自身就是一个handler,查看ThreadGroup的源码就可以发现,ThreadGroup实现了Thread.UncaughtExceptionHandler接口,并实现了默认的处理方法。默认的未捕获异常处理器处理时,会调用 System.err 进行输出,也就是直接打印到控制台了。

public void uncaughtException(Thread t, Throwable e) {
        if (parent != null) { // 父级优先处理
            parent.uncaughtException(t, e);
        } else {
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) { // 没有配置handler时的处理方式
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }

自定义异常处理:
Thread提供了一个setUncaughtExceptionHandler(), 我们只需要将自定义未捕获的异常处理器作为参数传入进去就可以了。

/**
 * @describe  自定义异常处理
 * @author Li DongWei
 */
public class Test1 {

    public static void main(String[] args) throws InterruptedException {    
        MyThread myThread = new MyThread();
        Thread th1 = new Thread(myThread);
        
        th1.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("捕获线程抛出的异常!");
            }
        });
        th1.start();
    }   

}
class MyThread implements Runnable{
    
    private int i = 10;
    @Override
    public void run() {
        //发生异常
        int i = 1 / 0;
    }
}

控制台输出


image.png

七、死锁的解决方案

什么是死锁

两个或多个线程互相持有对方需要的锁而导致这些线程全部处于永久阻塞状态。如:线程A持有对象1的锁,等待对象2的锁;线程B持有对象2的锁,等待对象1的锁。

发生死锁的四个必要条件

互斥:对于访问某些公共资源的线程需实现线程同步,即不能同时访问。
不剥夺:未使用完不可强行剥夺
请求和保持:进程至少持有一个资源同时要请求新的资源。
环路等待:存在一个线程-资源环形链。在进程集合{p0,p1...pn}中,p0正在等待p1占用的资源,p1等待p2占用的资源...pn等待p0占用的资源。

我们强调所有四个条件必须同时成立才会出现死锁。环路等待条件意味着请求和保持条件,这样四个条件并不完全独立。

解决死锁的三种方案:
1.资源排序
2.加锁时限
如果一个线程没有在指定的时间期限内获取到锁,则结束当前线程并释放掉已获得的锁。终止线程的方法:stop()会释放掉锁但易导致数据不一致。suspend()终止线程但不会释放掉锁。
3.死锁检测

文章参考:多线程异常处理机制
如何写一个死锁?发生死锁的三中解决方案

上一篇

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,574评论 1 15
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 3,086评论 1 18
  • 此片文章主要总结的是Thread类及相关的基础概念和API,首先需要厘清线程调度中的几个基本概念: 一、线程调度的...
    千淘萬漉阅读 2,686评论 0 2
  • 前言:虽然自己平时都在用多线程,也能完成基本的工作需求,但总觉得,还是对线程没有一个系统的概念,所以,查阅了一些资...
    justCode_阅读 791评论 0 9
  • 一、认识多任务、多进程、单线程、多线程 要认识多线程就要从操作系统的原理说起。 以前古老的DOS操作系统(V 6....
    GT921阅读 1,072评论 0 3

友情链接更多精彩内容