当一个线程池中的线程在执行过程中抛出异常时,该线程会发生什么?

11

当一个线程在线程池中执行时抛出异常会发生什么?

它会终止并成为垃圾回收吗?

背景: 我正在使用ScheduledThreadPoolExecutor来定期运行任务,并且Netty也在使用它。现在,使用此应用程序的客户端告诉我,他们有时会注意到一堆客户端被随机断开连接,每次这种情况发生时,都会记录下一个与Netty无关的任务的堆栈跟踪。我的假设是线程被中断并垃圾回收,因此Netty失去了对线程的任何引用并断开了分配的TCP客户端的连接。


1
你是在使用execute还是submit?因为异常处理不同,而且submit会吞噬异常。请发布最小的代码片段以了解问题。 - Ravindra babu
@Ravindrababu ScheduledExecutorService.schedule(new Runnable(), delay, TimeUnit.MILLISECONDS);); - Wesley
提醒@Wesley,如果答案有帮助,请接受它。 - Gray
2个回答

14
当线程池执行任务时,如果线程抛出异常,则线程的处理方式取决于使用的是threadPool.execute(...)还是threadPool.submit(...)。如果使用execute(...)且任务抛出未捕获的异常,则线程终止,线程池忘记该线程并立即启动另一个线程(如果适用)。任务和线程可以被垃圾回收。如果使用submit(...),则任务将完成,异常将被捕获,线程将继续运行,并出队下一个提交到线程池的作业并执行它。您可以通过扩展ThreadPoolExecutor并覆盖以下内容来查看抛出的异常:
protected void afterExecute(Runnable r, Throwable t) { }

使用submit(...)时,线程池会捕获任务抛出的所有异常,因此可以使用Future报告任务的状态。如果使用execute(...),则线程将终止并且它和任务可以被垃圾回收。如果使用submit(...),则线程不会终止,但是任务可以被垃圾回收。现在有不同的方式来减少运行中的线程数,这可能会导致线程终止并且可以被垃圾回收。
似乎有一些其他问题,如果一堆客户端同时终止。是指有什么东西干扰了它们吗?还是你想知道它们是否抛出了异常?
如果线程被中断,则某人正在取消作业或正在关闭线程池。除此之外,没有人应该中断线程。无论如何,这都不会导致线程终止并且可以被垃圾回收。如果线程被中断,当然任务可能会继续运行,除非它抛出了RuntimeException或其他异常。如果发生这种情况,线程池会在任务完成后清除中断,并从队列中取下一个任务。
您可以使用以下代码查看异常处理:
public static void main(String[] args) {
    ExecutorService threadPool = Executors.newFixedThreadPool(1);
    // this thread will be killed and replaced
    threadPool.execute(new Task());
    // this thread will catch and handle the exception and run the following task
    threadPool.submit(new Task());
    threadPool.submit(new Task());
}

private static class Task implements Runnable {
    @Override
    public void run() {
        // this should print out the same thread ids even though the task threw
        System.out.println(Thread.currentThread().getId());
        throw new RuntimeException();
    }
}

0
当线程池中的线程抛出异常时会发生什么?它会终止并被垃圾回收吗?
如果死亡的线程将线程池大小减少到小于核心池大小,则会立即创建一个新线程(因此您的旧线程将可供垃圾回收)。
请参见下面的代码:
当我使用核心池大小为“1”创建线程池时,当我的运行线程抛出运行时异常时,立即创建了一个新线程。
当我使用核心池大小为“2”创建线程池时,当我的运行线程抛出运行时异常时,立即创建了一个新线程。

import java.util.Scanner;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ThreadExample1 {


    private volatile ThreadPoolExecutor threadPoolExecutor;

    public static void main(String[] args) throws InterruptedException {
        ThreadExample1 threadExample1 = new ThreadExample1();
        threadExample1.doTheWork();
    }

    private void doTheWork() throws InterruptedException {
        Scanner scanner = new Scanner(System.in);


        ThreadFactory myThreadFactory = new MyThreadFactory("task");
        threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2, myThreadFactory);

        System.out.println("before task is sent to be executed:  thread pool size: " + threadPoolExecutor.getPoolSize() + ", core pool size: " + threadPoolExecutor.getCorePoolSize() + " - threads active count: " + threadPoolExecutor.getActiveCount());
        scanner.next();
        threadPoolExecutor.execute(new MyTask());

        System.out.println("immediately after task is sent to be executed: thread pool size: " + threadPoolExecutor.getPoolSize() + ", core pool size: " + threadPoolExecutor.getCorePoolSize() + " - threads active count: " + threadPoolExecutor.getActiveCount());
        scanner.next();

        System.out.println("going to sleep");
        Thread.sleep(2000);
        System.out.println("going to sleep");
        System.out.println("after waking up");
        System.out.println("after waking up: thread pool size: " + threadPoolExecutor.getPoolSize() + ", core pool size: " + threadPoolExecutor.getCorePoolSize() + " - threads active count: " + threadPoolExecutor.getActiveCount());
        scanner.next();

        threadPoolExecutor.shutdown();
        threadPoolExecutor.awaitTermination(5, TimeUnit.SECONDS);

    }

    class MyThreadFactory implements ThreadFactory {

        Logger logger = Logger.getLogger("MyThreadFactory");

        private int counter = 0;
        private String prefix = "";

        public MyThreadFactory(String prefix) {
            this.prefix = prefix;
        }

        @Override
        public Thread newThread(Runnable r) {
            counter++;
            String name = prefix + "-" + counter;
            logger.log(Level.WARNING, "thread: " + name + " - is going to be created");
            System.out.println("before a new thread is created. pool size: " + threadPoolExecutor.getPoolSize() + ", core pool size: " + threadPoolExecutor.getCorePoolSize() + " - threads active count: " + threadPoolExecutor.getActiveCount());
            return new Thread(r, name);
        }
    }


}


class MyTask implements Runnable {

    Logger logger = Logger.getLogger("MyTask");

    @Override
    public void run() {

        while (true) {

            System.out.println("thread: " + Thread.currentThread().getName() + " - current state: " + Thread.currentThread().getState());
            throw new RuntimeException("something bad happened");
        }

    }
}


我认为你对垃圾回收的理解不正确。一旦线程池替换了核心线程,原始线程就会被回收。你在代码中看到了吗? - Gray
如果一个核心线程终止,那么工作线程将立即被添加。请参阅ThreadPoolExecutor.processWorkerExit()。 - Gray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接