How to manage pool thread termination in ThreadPoolExecutor?

北战南征 提交于 2019-12-25 07:50:40

问题


Quote from concurrency in practice:

To set an UncaughtExceptionHandler for pool threads, provide a ThreadFactory to the ThreadPoolExecutor constructor. (As with all thread manipulation, only the thread's owner should change its UncaughtExceptionHandler.) The standard thread pools allow an uncaught task exception to terminate the pool thread, but use a try-finally block to be notified when this happens so the thread can be replaced. Without an uncaught exception handler or other failure notification mechanism, tasks can appear to fail silently, which can be very confusing. If you want to be notified when a task fails due to an exception so that you can take some task-specific recovery action, either wrap the task with a Runnable or Callable that catches the exception or override the afterExecute hook in THReadPoolExecutor.

Book doesn't provide any examples how to achieve this.

Can you show this technique ?

P.S.

I tried to write code sample:

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);
}

but this output

created thread with id 1
created thread with id 1

always.


回答1:


There are several errors in your code. First, you must pass the Runnable passed to your ThreadFactory to the created thread, otherwise, you leave a broken thread not executing any tasks. Second, you are printing the id of Thread.currentThread() in your factory, which is obviously not the freshly created thread. That’s why it prints an id of 1 two times.

Still, after fixing these errors, you won’t see uncaught exceptions. The reason is that while the thread pool executor behaves as stated, the submit methods wrap your Runnable into a FutureTask that catches all exceptions on its own, so that they can be reported when calling get().

At this point, we have to remember that there is a queue of arbitrary Runnables, which don’t have to be FutureTask instances. So uncaught exceptions are still possible, e.g. when we enqueue runnables directly via execute.

So the fixed example:

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();
    }
}

will print

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)

Of course, the numbers and the order of the messages may differ.

As stated in the cite, you can also learn about exceptions by overriding afterExecute:

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4, 1,
    TimeUnit.MINUTES, new LinkedBlockingQueue<>()) {
        @Override protected void afterExecute(Runnable r, Throwable t) {
            if(t!=null) synchronized(System.out) {
               System.out.println("Uncaught exception in "+Thread.currentThread().getId());
               t.printStackTrace(System.out);
            }
        }
    };

Though, in my tests, the default uncaught exception handler also printed stack traces…



来源:https://stackoverflow.com/questions/42390533/how-to-manage-pool-thread-termination-in-threadpoolexecutor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!