如何在ThreadPoolExecutor中管理池线程终止?

时间:2017-02-22 11:48:30

标签: java multithreading concurrency exception-handling

在实践中引用并发:

  

要为池线程设置UncaughtExceptionHandler,请提供   ThreadFactory到ThreadPoolExecutor构造函数。 (和所有人一样   线程操作,只有线程的所有者才应该更改它   UncaughtExceptionHandler。)标准线程池允许未被捕获   任务异常终止池线程,但使用try-finally   阻止在发生这种情况时收到通知,以便更换线程。   没有未捕获的异常处理程序或其他故障通知   机制,任务可能看起来无声地失败,这可能是非常的   混乱。如果您希望在任务因某个问题而失败时收到通知   例外,以便您可以执行某些特定于任务的恢复操作,   用可捕获的Runnable或Callable包装任务   异常或覆盖THReadPoolExecutor中的afterExecute挂钩。

Book没有提供任何如何实现这一目的的例子。

你能展示这种技术吗?

P.S。

我尝试编写代码示例:

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread();
            thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(Thread t, Throwable e) {
                    e.printStackTrace();
                }
            });
            System.out.println("created thread with id " + Thread.currentThread().getId());
            return thread;
        }
    });
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getId() + " started");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getId() + " termination");
            throw new RuntimeException();

        }
    };
    threadPoolExecutor.submit(runnable);
    threadPoolExecutor.submit(runnable);
    threadPoolExecutor.submit(runnable);
    threadPoolExecutor.submit(runnable);
}

但是这个输出

created thread with id 1
created thread with id 1

总是

1 个答案:

答案 0 :(得分:1)

您的代码中存在多个错误。首先,您必须将传递给Runnable的{​​{1}}传递给创建的线程,否则,您将保留一个不执行任何任务的断线。其次,您在工厂中打印ThreadFactory的id,这显然不是新创建的线程。这就是为什么它打印两次Thread.currentThread()的id。

但是,在修复这些错误后,您将看不到未捕获的异常。原因是虽然线程池执行程序的行为符合规定,但1方法将submit包装到Runnable中,以便自己捕获所有异常,以便在调用时报告它们FutureTask

此时,我们必须记住,有一个任意get()的队列,它不必是Runnable个实例。所以未被捕获的例外仍然是可能的,例如当我们通过FutureTask直接排队runnables时。

所以固定的例子:

execute

将打印

public class Test {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4, 1,
                TimeUnit.MINUTES, new LinkedBlockingQueue<>(), r -> {
                    Thread thread = new Thread(r);
                    thread.setUncaughtExceptionHandler((t, e) -> {
                        synchronized(System.out) {
                            System.out.println("Uncaught exception in "+t.getId());
                            e.printStackTrace(System.out);
                        }
                    });
                    System.out.println("created thread with id " + thread.getId());
                    return thread;
        });
        Runnable runnable = () -> {
            System.out.println(Thread.currentThread().getId() + " started");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getId() + " termination");
            throw new RuntimeException();
            };
        threadPoolExecutor.execute(runnable);
        threadPoolExecutor.execute(runnable);
        threadPoolExecutor.execute(runnable);
        threadPoolExecutor.execute(runnable);
        threadPoolExecutor.shutdown();
    }
}

当然,消息的数量和顺序可能不同。

如引用中所述,您还可以通过覆盖created thread with id 11 created thread with id 12 11 started 12 started 11 termination 12 termination created thread with id 14 created thread with id 15 Uncaught exception in 11 java.lang.RuntimeException at smp.Test.lambda$main$2(Test.java:28) 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) 15 started 14 started Uncaught exception in 12 java.lang.RuntimeException at smp.Test.lambda$main$2(Test.java:28) 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) 14 termination 15 termination Uncaught exception in 14 java.lang.RuntimeException at smp.Test.lambda$main$2(Test.java:28) 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) Uncaught exception in 15 java.lang.RuntimeException at smp.Test.lambda$main$2(Test.java:28) 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) 了解异常:

afterExecute

尽管如此,在我的测试中,默认的未捕获异常处理程序也打印了堆栈跟踪...