当前位置: 代码网 > it编程>编程语言>Java > Java线程池执行过程中遇到异常的原因和解决办法

Java线程池执行过程中遇到异常的原因和解决办法

2025年10月11日 Java 我要评论
线程遇到未处理的异常就结束了这个好理解,当线程出现未捕获异常的时候就执行不下去了,留给它的就是垃圾回收了。线程池中线程频繁出现未捕获异常当线程池中线程频繁出现未捕获的异常,那线程的复用率就大大降低了,

线程遇到未处理的异常就结束了

这个好理解,当线程出现未捕获异常的时候就执行不下去了,留给它的就是垃圾回收了。 

线程池中线程频繁出现未捕获异常

当线程池中线程频繁出现未捕获的异常,那线程的复用率就大大降低了,需要不断地创建新线程。
做个实验:

public class threadexecutor {
    private threadpoolexecutor threadpoolexecutor = new threadpoolexecutor(1, 1, 60, timeunit.seconds,
                                                                           new arrayblockingqueue<>(200), new threadfactorybuilder().setnameformat("customthread %d").build());
    @test
    public void test() {
        intstream.rangeclosed(1, 5).foreach(i -> {
            try {
                thread.sleep(100);
            } catch (interruptedexception e) {
                e.printstacktrace();
            }
            threadpoolexecutor.execute(() -> {
                int j = 1/0;
            });});
    }
}

新建一个只有一个线程的线程池,每隔0.1s提交一个任务,任务中是一个1/0的计算。

exception in thread "customthread 0" java.lang.arithmeticexception: / by zero
 at thread.threadexecutor.lambda$null$0(threadexecutor.java:25)
 at java.util.concurrent.threadpoolexecutor.runworker(threadpoolexecutor.java:1142)
 at java.util.concurrent.threadpoolexecutor$worker.run(threadpoolexecutor.java:617)
 at java.lang.thread.run(thread.java:748)
exception in thread "customthread 1" java.lang.arithmeticexception: / by zero
 at thread.threadexecutor.lambda$null$0(threadexecutor.java:25)
 at java.util.concurrent.threadpoolexecutor.runworker(threadpoolexecutor.java:1142)
 at java.util.concurrent.threadpoolexecutor$worker.run(threadpoolexecutor.java:617)
 at java.lang.thread.run(thread.java:748)
exception in thread "customthread 2" java.lang.arithmeticexception: / by zero
 at thread.threadexecutor.lambda$null$0(threadexecutor.java:25)
 at java.util.concurrent.threadpoolexecutor.runworker(threadpoolexecutor.java:1142)
 at java.util.concurrent.threadpoolexecutor$worker.run(threadpoolexecutor.java:617)
 at java.lang.thread.run(thread.java:748)
exception in thread "customthread 3" java.lang.arithmeticexception: / by zero
 at thread.threadexecutor.lambda$null$0(threadexecutor.java:25)
 at java.util.concurrent.threadpoolexecutor.runworker(threadpoolexecutor.java:1142)
 at java.util.concurrent.threadpoolexecutor$worker.run(threadpoolexecutor.java:617)
 at java.lang.thread.run(thread.java:748)
exception in thread "customthread 4" java.lang.arithmeticexception: / by zero
 at thread.threadexecutor.lambda$null$0(threadexecutor.java:25)
 at java.util.concurrent.threadpoolexecutor.runworker(threadpoolexecutor.java:1142)
 at java.util.concurrent.threadpoolexecutor$worker.run(threadpoolexecutor.java:617)
 at java.lang.thread.run(thread.java:748)
exception in thread "customthread 5" java.lang.arithmeticexception: / by zero
 at thread.threadexecutor.lambda$null$0(threadexecutor.java:25)
 at java.util.concurrent.threadpoolexecutor.runworker(threadpoolexecutor.java:1142)
 at java.util.concurrent.threadpoolexecutor$worker.run(threadpoolexecutor.java:617)
 at java.lang.thread.run(thread.java:748)

可见每次执行的线程都不一样,之前的线程都没有复用。原因是因为出现了未捕获的异常。
把异常捕获试试:

public class threadexecutor {
    private threadpoolexecutor threadpoolexecutor = new threadpoolexecutor(1, 1, 60, timeunit.seconds,
                                                                           new arrayblockingqueue<>(200), new threadfactorybuilder().setnameformat("customthread %d").build());
    @test
    public void test() {
        intstream.rangeclosed(1, 5).foreach(i -> {
            try {
                thread.sleep(100);
            } catch (interruptedexception e) {
                e.printstacktrace();
            }
            threadpoolexecutor.execute(() -> {
                try {
                    int j = 1 / 0;
                } catch (exception e) {
                    system.out.println(thread.currentthread().getname() +" "+ e.getmessage());
                }
            });
        });
    }
}
customthread 0 / by zero
customthread 0 / by zero
customthread 0 / by zero
customthread 0 / by zero
customthread 0 / by zero

可见当异常捕获了,线程就可以复用了。 

问题来了,代码中异常不可能全部捕获

如果要捕获那些没被业务代码捕获的异常,可以设置thread类的uncaughtexceptionhandler属性。这时使用threadfactorybuilder会比较方便,threadfactorybuilder是guava提供的threadfactory生成器。

new threadfactorybuilder()
    .setnameformat("customthread %d")
    .setuncaughtexceptionhandler((t, e) -> system.out.println(t.getname() + "发生异常" + e.getcause()))
    .build()

修改之后:

public class threadexecutor {
    private static threadpoolexecutor threadpoolexecutor = new threadpoolexecutor(1, 1, 60, timeunit.seconds,
                                                                                  new arrayblockingqueue<>(200),
                                                                                  new threadfactorybuilder()
                                                                                  .setnameformat("customthread %d")
                                                                                  .setuncaughtexceptionhandler((t, e) -> system.out.println("uncaughtexceptionhandler捕获到:" + t.getname() + "发生异常" + e.getmessage()))
                                                                                  .build());
    @test
    public void test() {
        intstream.rangeclosed(1, 5).foreach(i -> {
            try {
                thread.sleep(100);
            } catch (interruptedexception e) {
                e.printstacktrace();
            }
            threadpoolexecutor.execute(() -> {
                system.out.println("线程" + thread.currentthread().getname() + "执行");
                int j = 1 / 0;
            });
        });
    }
}
线程customthread 0执行
uncaughtexceptionhandler捕获到:customthread 0发生异常/ by zero
线程customthread 1执行
uncaughtexceptionhandler捕获到:customthread 1发生异常/ by zero
线程customthread 2执行
uncaughtexceptionhandler捕获到:customthread 2发生异常/ by zero
线程customthread 3执行
uncaughtexceptionhandler捕获到:customthread 3发生异常/ by zero
线程customthread 4执行
uncaughtexceptionhandler捕获到:customthread 4发生异常/ by zero

可见,结果并不是想象的那样,线程池中原有的线程没有复用!所以通过uncaughtexceptionhandler想将异常吞掉使线程复用这招貌似行不通。它只是做了一层异常的保底处理。
excute改成submit试试

public class threadexecutor {
    private static threadpoolexecutor threadpoolexecutor = new threadpoolexecutor(1, 1, 60, timeunit.seconds,
                                                                                  new arrayblockingqueue<>(200),
                                                                                  new threadfactorybuilder()
                                                                                  .setnameformat("customthread %d")
                                                                                  .setuncaughtexceptionhandler((t, e) -> system.out.println("uncaughtexceptionhandler捕获到:" + t.getname() + "发生异常" + e.getmessage()))
                                                                                  .build());
    @test
    public void test() {
        intstream.rangeclosed(1, 5).foreach(i -> {
            try {
                thread.sleep(100);
            } catch (interruptedexception e) {
                e.printstacktrace();
            }
            future<?> future = threadpoolexecutor.submit(() -> {
                system.out.println("线程" + thread.currentthread().getname() + "执行");
                int j = 1 / 0;
            });
            try {
                future.get();
            } catch (interruptedexception e) {
                e.printstacktrace();
            } catch (executionexception e) {
                e.printstacktrace();
            }
        });
    }
}
线程customthread 0执行
java.util.concurrent.executionexception: java.lang.arithmeticexception: / by zero
线程customthread 0执行
java.util.concurrent.executionexception: java.lang.arithmeticexception: / by zero
线程customthread 0执行
java.util.concurrent.executionexception: java.lang.arithmeticexception: / by zero
线程customthread 0执行
java.util.concurrent.executionexception: java.lang.arithmeticexception: / by zero
线程customthread 0执行
java.util.concurrent.executionexception: java.lang.arithmeticexception: / by zero

通过submit提交线程可以屏蔽线程中产生的异常,达到线程复用。当get()执行结果时异常才会抛出。
原因是通过submit提交的线程,当发生异常时,会将异常保存,待future.get();时才会抛出。
这是futuretask的部分run()方法,看setexception

public void run() {
    try {
        callable<v> 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);
        }
    } 
}
protected void setexception(throwable t) {
    if (unsafe.compareandswapint(this, stateoffset, new, completing)) {
        outcome = t;
        unsafe.putorderedint(this, stateoffset, exceptional); // final state
        finishcompletion();
    }
}

将异常存在outcome对象中,没有抛出,再看get方法:

public v get() throws interruptedexception, executionexception {
    int s = state;
    if (s <= completing)
        s = awaitdone(false, 0l);
    return report(s);
}
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);
}

当outcome是异常时才抛出。 

总结

1、线程池中线程中异常尽量手动捕获

2、通过设置threadfactoryuncaughtexceptionhandler可以对未捕获的异常做保底处理,通过execute提交任务,线程依然会中断,而通过submit提交任务,可以获取线程执行结果,线程异常会在get执行结果时抛出。

以上就是java线程池执行过程中遇到异常的原因和解决办法的详细内容,更多关于java线程池执行遇到异常的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com