美文网首页
消失的线程

消失的线程

作者: xpbob | 来源:发表于2018-12-31 13:46 被阅读11次

很多小伙伴都问过我一个问题,就是任务线程跑着跑着消失了,而且没有任何异常日志。我都是条件反射式的回复,是不是用了线程池的submit提交任务。而且很大几率对方给予肯定答复。
解决方案,很多人都听过不少,下面我就分析一下原因以及最佳实践。

为什么消失

submit这个单词用的真的特别好,特别洋气,虽然可以用execute来提交,但是大部分人都是用的submit。问题也就出在submit上了。

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

submit也是调用的execute,而且是有Future的返回值的。他把一个Runnable 组装成了一个无返回类型的FutureTask。
FutureTask的run方法与众不同。

    public void run() {
        ....
        try {
            Callable c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            ....
        }
    }

可以看到FutureTask的run自己捕获了Throwable 而且没有抛出,线程里的所有代码出了什么错都会被捕获,并且赋值给了成员变量outcome。
最终在get里调用report有一段逻辑可以取出异常

    private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

最后异常会在get的时候throw。
程序出错,异常被吞掉了,最终展现出业务线程消失的现象。

解决方案

run里自己编码解决异常

自己的代码健壮到一言不合来个大写的try catch 而且一定是Throwable并且要合理的记录 。写这种代码真心累,而且在一些特殊场合才这么干,例如javaagent。搞个探针上去,一堆异常在业务系统上,换谁谁都怕。

future.get()

submit的时候会返回一个Future,我们只要get就可以获取到异常。这个是一个理论可行的方案,这个也只用在有交互的场景,例如出现异常了再次提交任务等等。再交互的场景下,这个方案特别适合。

execute提交

submit最终也调用的execute。execute只能接收参数Runnable。
execute直接把异常给抛出了,就是不怎么优雅的记录。

        try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }

每个work执行的时候虽然也会捕获各种异常,但是最后都throw出来了,也能达到追踪的效果。

自定义线程池

继承线程池然后复写afterExecute(task, thrown);上面也看到了最后会执行这个方法,而且可以优雅的记录日志,以及可以做一些补偿操作再里面。

最佳实践

一般使用线程池都会自己去自定义,毕竟为了区别,我们会自己去写线程工厂去标识自己的线程,而且为了内存估计会调整阻塞队列的类型和大小,在一些压力突增的情况还得控制线程池里最大线程的个数与核心线程的比率,所以线程池的自定义是很有必要的。推荐使用自定义线程池的方式记录日志。并不推荐补偿等逻辑也写在线程池里,对于要补偿的情况推荐future.get()。这里只写异常的处理和补偿即可,日志就不用记录了。

相关文章

  • 消失的线程

    很多小伙伴都问过我一个问题,就是任务线程跑着跑着消失了,而且没有任何异常日志。我都是条件反射式的回复,是不是用了线...

  • ThreadLocal 深入了解

    ThreadLocal用于提供线程局部变量,每个线程有拥有其独立初始化的变量副本。当线程消失时,其线程本地实例的所...

  • JAVA技术分享:消失的线程

    很多小伙伴都问过我一个问题,就是任务线程跑着跑着消失了,而且没有任何异常日志。我都是条件反射式的回复,是不是用了线...

  • 鸿蒙系统线程管理

    概述 在启动应用时,系统会为该应用创建一个主线程。该线程随着应用创建或消失,界面的显示和更新等操作,都是在主线程上...

  • 深入理解JVM垃圾收集机制,下次面试你准备好了吗

    程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需...

  • jvm-垃圾收集器与内存分配策略

    因为程序计数器,栈,都是随着线程的开启而开启,随着线程的消失而销毁,所以这部分基本不需要垃圾回收器来操心,剩下的就...

  • Android弹窗组件工作机制之Dialog、DialogFra

    二、Dialog的消失 1、dismiss 保证UI操作都在主线程执行,而且引用了Java8新特性写法this::...

  • RunLoop解决卡顿问题

    人生就像RunLoop,不断的循环、不断的往复。当线程被杀掉,当生命结束,RunLoop就消失了,人生也就结束了。...

  • iOS 定时无法销毁

    我们在系统开辟的runloop 线程中,在视图消失的时候关闭定时器是无法关闭的,尝试一下这个方法。[timer s...

  • 消失,都会消失的

    万山碧绿被红色巨兽疯狂的吃咬,不停的咀嚼声将所有叫做生命的东西嚼碎,吸掉它们的灵魂,只剩下漆黑的残灰。一只老虎拖...

网友评论

      本文标题:消失的线程

      本文链接:https://www.haomeiwen.com/subject/ojgilqtx.html