【问题标题】:What happens to a Thread when it throws an exception when executed by a thread pool?当线程被线程池执行时抛出异常会发生什么?
【发布时间】:2017-11-18 04:46:34
【问题描述】:

当线程被线程池执行时抛出异常会发生什么?

它会终止并被垃圾收集吗?

背景故事: 我不时使用ScheduledThreadPoolExecutor 运行任务,Netty 也在使用它。现在我使用这个应用程序的客户告诉我,他们有时会注意到一堆客户端随机断开连接,每次发生这种情况时,都会记录来自单独的非网络相关任务的堆栈跟踪。我的假设是线程被中断并被垃圾收集,因此 Netty 丢失了对线程的任何引用并断开分配的 tcp 客户端。

【问题讨论】:

  • 您使用的是执行还是提交?因为异常处理不同而提交燕子异常。 stackoverflow.com/questions/3929342/…。发布最少的代码 sn-p 以了解问题。
  • @Ravindrababu ScheduledExecutorService.schedule(new Runnable(), delay, TimeUnit.MILLISECONDS););
  • 如果对@Wesley 有帮助,提醒您接受答案。

标签: java multithreading threadpool


【解决方案1】:

当线程被线程池执行时抛出异常会发生什么?

它会终止并被垃圾回收吗?

如果死掉的线程将线程池大小减小到小于核心池大小,则立即创建一个新线程(因此您的旧线程将可用于垃圾回收)

见下面的代码:

当我创建一个 core pool size = "1" 的线程池时,当我的运行线程抛出运行时异常时,会立即创建一个新线程。

当我创建一个 core pool size = "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");
        }

    }
}

【讨论】:

  • 我认为您对垃圾收集的看法不正确。一旦线程池替换了一个核心线程,就会收集原来的线程。你在代码中看到了吗?
  • 如果它是终止的核心线程,也会立即添加worker。请参阅 ThreadPoolExecutor.processWorkerExit()。
【解决方案2】:

当线程被线程池执行时抛出异常会发生什么?

这个问题的答案取决于代码是使用threadPool.execute(...) 还是threadPool.submit(...)。如果您使用execute(...) 并且任务抛出未捕获的异常,则线程终止并且池忘记该线程并在适当时立即启动另一个线程。任务和线程可以进行垃圾回收。

如果您使用submit(...),则任务将完成,异常将被捕获,线程将继续运行并将下一个提交到线程池的作业出列并执行。您可以通过扩展ThreadPoolExecutor 并覆盖来查看引发了什么异常:

protected void afterExecute(Runnable r, Throwable t) { }

使用submit(...),线程池捕获任务抛出的所有异常,因此它可以使用Future 报告任务的状态。

它会终止并被垃圾回收吗?

如果使用execute(...),那么是的,线程将终止并且它和任务可以被垃圾收集。如果使用submit(...),则线程不会终止,但可以对任务进行垃圾收集。现在,池可以通过不同的方式来决定减少其中运行的线程数,这可能会导致线程终止并可能会被垃圾回收。

...一堆客户端随机断开连接...

意思是有什么东西打断了他们?或者你想知道他们是否抛出异常?如果一堆同时终止,似乎正在发生其他事情。

我的假设是线程被中断并被垃圾收集,因此 Netty 丢失了对线程的任何引用并断开分配的 tcp 客户端。

如果线程被中断,那么有人正在对作业调用取消或正在关闭线程池。没有其他人应该中断线程。无论如何,这不会导致线程被终止并被垃圾收集。如果线程被中断,任务当然可以继续运行,除非它抛出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();
    }
}

【讨论】:

    猜你喜欢
    • 2010-12-12
    • 2013-06-09
    • 1970-01-01
    • 2010-12-22
    • 2015-02-13
    • 2021-04-11
    • 1970-01-01
    • 2015-08-20
    • 1970-01-01
    相关资源
    最近更新 更多