线程池中的线程异常

2021-03-29  本文已影响0人  virtual灬zzZ

一个线程池中的线程异常了,那么线程池会怎么处理这个线程?

首先进行模拟,

import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

public class ExecutorsTest {
    public static void main(String[] args) {
        ThreadPoolTaskExecutor executorService = buildThreadPoolTaskExecutor();
        executorService.execute(() -> sayHi("execute"));
        Future<?> submit = executorService.submit(() -> sayHi("submit"));
        try {
            submit.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    private static void sayHi(String name) {
        String printStr = "【thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name + "】";
        System.out.println(printStr);
        throw new RuntimeException(printStr + ",抛出异常!!");
    }

    private static ThreadPoolTaskExecutor buildThreadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executorService = new ThreadPoolTaskExecutor();
        executorService.setThreadNamePrefix("(ThreadTest)-");
        executorService.setCorePoolSize(5);
        executorService.setMaxPoolSize(10);
        executorService.setQueueCapacity(1000);
        executorService.setKeepAliveSeconds(30);
        executorService.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executorService.initialize();
        return executorService;
    }
}

结果如下:

Exception in thread "(ThreadTest)-1" java.lang.RuntimeException: 【thread-name:(ThreadTest)-1,执行方式:execute】,抛出异常!
    at cn.starcart.open.openapi.constants.remoteurl.ExecutorsTest.sayHi(ExecutorsTest.java:25)
    at cn.starcart.open.openapi.constants.remoteurl.ExecutorsTest.lambda$main$0(ExecutorsTest.java:10)
    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:745)
【thread-name:(ThreadTest)-1,执行方式:execute】
【thread-name:(ThreadTest)-2,执行方式:submit】

由结果可见

当执行方式是execute时,可以看到堆栈异常的输出。

当执行方式是submit时,堆栈异常没有输出。

java.util.concurrent.ThreadPoolExecutor#runWorker中抛出了异常:

QQ图片20210329113715.png

java.lang.ThreadGroup#uncaughtException进行了异常处理:

QQ图片20210329114522.png

这个方法是JVM调用的,我们只需要指定我们想要的处理方式即可。

那我们怎么指定呢:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class test {
    public static void main(String[] args) {
        Runnable runnable = () -> {
            throw new RuntimeException("runtimeEx");
        };


        //直接new Thread()的时候
        Thread t = new Thread(runnable);
        t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("hahha=" + e.getMessage());
            }
        });

        t.start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //线程池的时候:
        ExecutorService threadPool = Executors.newFixedThreadPool(2, r -> {
            Thread temp = new Thread(r);
            temp.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(Thread t, Throwable e) {
                    System.out.println("hahhapool=" + e.getMessage());
                }
            });
            return temp;
        });

        threadPool.execute(runnable);
    }
}

运行结果:
hahha=runtimeEx
hahhapool=runtimeEx

QQ图片20210329164810.png
其本质也是调用了execute方法,所以它还是回到java.util.concurrent.ThreadPoolExecutor#runWorker方法:

不影响其他线程任务

ThreadPoolTaskExecutor executorService = buildThreadPoolTaskExecutor();
        executorService.execute(() -> sayHi("execute"));
        //executorService.submit(() -> sayHi("submit"));

        Future<?> submit = executorService.submit(() -> sayHi("submit"));
        try {
            submit.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        executorService.execute(() -> sayHi("55"));

        executorService.execute(() -> sayHi("execute1"));

        executorService.execute(() -> sayHi("execute2"));

结果:

【thread-name:(ThreadTest)-2,执行方式:submit】
【thread-name:(ThreadTest)-5,执行方式:execute2】
Exception in thread "(ThreadTest)-3" java.lang.RuntimeException: 【thread-name:(ThreadTest)-3,执行方式:55】,抛出异常!
    at cn.starcart.open.openapi.constants.remoteurl.ExecutorsTest.sayHi(ExecutorsTest.java:35)
    at cn.starcart.open.openapi.constants.remoteurl.ExecutorsTest.lambda$main$2(ExecutorsTest.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:745)
【thread-name:(ThreadTest)-4,执行方式:execute1】
【thread-name:(ThreadTest)-1,执行方式:execute】

线程池中一个线程异常了后,不影响其他线程任务

大家注意线程名称这个细节:1,2,3,4,6。魔鬼都在细节里啊,这个点我下面会讲,先在这里把问题抛出来:我就纳闷了,怎么没有5啊?!

这个线程会被放回线程池为啥全错了?
我们去源码里面寻找答案:

让源码给出答案:

5号线程去哪里了?

new Worker()方法会告诉你:5去哪里了。

再配上这张由我这个灵魂画师亲自操刀画的图,一起食用,味道更佳:

现在我们知道为啥:我回答这个线程会被放回线程池为啥全错了吧。还附送你一个线程名称变化的细节,不客气,关注一下就好。

当一个线程池里面的线程异常后:

当执行方式是execute时,可以看到堆栈异常的输出。

当执行方式是submit时,堆栈异常没有输出。但是调用Future.get()方法时,可以捕获到异常。

不会影响线程池里面其他线程的正常执行。

线程池会把这个线程移除掉,并创建一个新的线程放到线程池中。

https://blog.csdn.net/javachengzi/article/details/108733679

上一篇 下一篇

猜你喜欢

热点阅读