使用 ThreadPoolExecutor 进行有状态异常处理



基本上,当你使用ThreadPoolExecutor时,有几种不同的策略来处理异常:

    Thread.setUncaughtExceptionHandler(
  1. ( (and Thread.getDefaultUncaughtExceptionHandler(((

    Exception 包装在 Future,因此永远不会调用 UncaughtExceptionHandler,因此不能使用它。

  2. 设置线程工厂唯一相关的部分是 newley 创建线程上的 Thread.setUncaughtExceptionHandler((。但这不会有任何效果,见第1页(。

  3. Overoverride ThreadPoolExecutor.afterExecute((

    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if (t == null && r instanceof Future<?>) {
            try {
                  Object result = ((Future<?>) r).get();
                } catch (CancellationException ce) {
                  t = ce;
                } catch (ExecutionException ee) {
                  t = ee.getCause();
                } catch (InterruptedException ie) {
                  Thread.currentThread().interrupt(); // ignore/reset
                }
        }
        if (t != null){
            logger.error("ThreadPoolExecutor.afterExecute", t);
        }
    }
    

    这种方法几乎有效。如果您的异常处理是无状态的,也就是说您不需要访问原始 Runnable/Callable 任务的状态,这没关系。在有状态的情况下,你无法访问原始任务(甚至反射也无济于事,因为上面的 Runnable 不会保存原始任务(。

当我想要访问原始任务的状态时,如何处理异常?

首先看处理 ThreadPoolExecutor 的异常,以获取有关 afterExecute(( 方法问题的更多背景信息。

ThreadPoolExecutor 有

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) ;

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value);

当可调用时,可运行是您可以装饰的原始任务。这是基本策略。下面是使用 Spring 的工作代码(为了清楚起见,我删除了注释(:

package org.springframework.scheduling.concurrent;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.springframework.core.task.TaskDecorator;
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
import org.springframework.scheduling.concurrent.ExecutorConfigurationSupport;
import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean;
import org.springframework.util.Assert;
public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {
    private final Object poolSizeMonitor = new Object();
    private int corePoolSize = 1;
    private int maxPoolSize = Integer.MAX_VALUE;
    private int keepAliveSeconds = 60;
    private int queueCapacity = Integer.MAX_VALUE;
    private boolean allowCoreThreadTimeOut = false;
    //fix
    private CallableTransform callableTransform;
    private ThreadPoolExecutor threadPoolExecutor;

    public void setCorePoolSize(int corePoolSize) {
        synchronized (this.poolSizeMonitor) {
            this.corePoolSize = corePoolSize;
            if (this.threadPoolExecutor != null) {
                this.threadPoolExecutor.setCorePoolSize(corePoolSize);
            }
        }
    }
    public int getCorePoolSize() {
        synchronized (this.poolSizeMonitor) {
            return this.corePoolSize;
        }
    }
    public void setMaxPoolSize(int maxPoolSize) {
        synchronized (this.poolSizeMonitor) {
            this.maxPoolSize = maxPoolSize;
            if (this.threadPoolExecutor != null) {
                this.threadPoolExecutor.setMaximumPoolSize(maxPoolSize);
            }
        }
    }
    public int getMaxPoolSize() {
        synchronized (this.poolSizeMonitor) {
            return this.maxPoolSize;
        }
    }
    public void setKeepAliveSeconds(int keepAliveSeconds) {
        synchronized (this.poolSizeMonitor) {
            this.keepAliveSeconds = keepAliveSeconds;
            if (this.threadPoolExecutor != null) {
                this.threadPoolExecutor.setKeepAliveTime(keepAliveSeconds, TimeUnit.SECONDS);
            }
        }
    }
    public int getKeepAliveSeconds() {
        synchronized (this.poolSizeMonitor) {
            return this.keepAliveSeconds;
        }
    }
    public void setQueueCapacity(int queueCapacity) {
        this.queueCapacity = queueCapacity;
    }
    public void setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
        this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
    }

    //fix
    public void setCallableDecorator(CallableDecorator callableDecorator) {
        Assert.isNull(this.callableTransform, "You can' call setCallableDecorator() and setTaskDecorator() more than once");
        this.callableTransform = new CallableTransform(){
            @Override
            public Callable<?> decorate(Object originalTask) {
                Callable<?> ret = callableDecorator.decorate((Callable<?>)originalTask);
                return ret;
            }
            @Override
            public boolean isCallable(){
                return true;
            }
    };
}
    //fix
    public void setTaskDecorator(TaskDecorator taskDecorator) {
        Assert.isNull(this.callableTransform, "You can' call setCallableDecorator() and setTaskDecorator() more than once");
        this.callableTransform =  new CallableTransform(){
            @Override
            public Callable<?> decorate(Object originalTask) {
                Callable<?> ret= Executors.callable(taskDecorator.decorate((Runnable)originalTask));
                return ret;
            }
            @Override
            public boolean isCallable(){
                return false;
            }
        };
    }

    @Override
    protected ExecutorService initializeExecutor(
            ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
        BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);
        ThreadPoolExecutor executor;
        //fix
        if (this.callableTransform != null) {
            executor = new ThreadPoolExecutor(
                    this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
                    queue, threadFactory, rejectedExecutionHandler) {
                @Override
                protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
                    if(callableTransform==null){
                        return super.newTaskFor(callable);
                    }
                    Callable<?> wrapedCallable = null;
                    boolean isCallable = callableTransform.isCallable();
                    if(isCallable){
                        wrapedCallable = callableTransform.decorate(callable);
                    } else {
                        //callableTransform accepts Runnable, but we have Callable
                        throw new IllegalStateException("You use TaskDecorator, but submit Callable");
                    }
                    @SuppressWarnings("unchecked")
                    Callable<T> param = (Callable<T>)wrapedCallable;
                    return super.newTaskFor(param);
                }
                @Override
                protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
                    if(callableTransform==null){
                        return super.newTaskFor(runnable, value);
                    }
                    Callable<?> wrapedCallable = null;
                    boolean isRunnable = callableTransform.isRunnable();
                    if(isRunnable){
                        wrapedCallable = callableTransform.decorate(runnable);
                    } else {
                        //callableTransform accepts Callable, but we have Runnable
                        throw new IllegalStateException("You use CallableDecorator, but execute Runnable");
                    }
                    @SuppressWarnings("unchecked")
                    Callable<T> param = (Callable<T>)wrapedCallable;
                    return super.newTaskFor(param);
                }

            };
        } else {
            executor = new ThreadPoolExecutor(
                    this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
                    queue, threadFactory, rejectedExecutionHandler);
        }
        if (this.allowCoreThreadTimeOut) {
            executor.allowCoreThreadTimeOut(true);
        }
        this.threadPoolExecutor = executor;
        return executor;
    }

    protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
        if (queueCapacity > 0) {
            return new LinkedBlockingQueue<>(queueCapacity);
        }
        else {
            return new SynchronousQueue<>();
        }
    }
    public ThreadPoolExecutor getThreadPoolExecutor() throws IllegalStateException {
        Assert.state(this.threadPoolExecutor != null, "ThreadPoolTaskExecutor not initialized");
        return this.threadPoolExecutor;
    }
    public int getPoolSize() {
        if (this.threadPoolExecutor == null) {
            // Not initialized yet: assume core pool size.
            return this.corePoolSize;
        }
        return this.threadPoolExecutor.getPoolSize();
    }
    public int getActiveCount() {
        if (this.threadPoolExecutor == null) {
            // Not initialized yet: assume no active threads.
            return 0;
        }
        return this.threadPoolExecutor.getActiveCount();
    }

    @FunctionalInterface
    public interface CallableDecorator {
        <V> Callable<V> decorate(Callable<V> task);
    }
    @FunctionalInterface
    static interface CallableTransform {
        Callable<?> decorate(Object originalTask);
        default boolean isCallable(){
            return true;
        }
        default boolean isRunnable(){
            return !isCallable();
        }
    }

    //rest of the code execute/submit override
    //...
    @Override
    public boolean prefersShortLivedTasks() {
        return true;
    }
}

使用示例是傻瓜:

    ThreadPoolTaskExecutor threadPoolFactory = new ThreadPoolTaskExecutor();
    threadPoolFactory.setCorePoolSize(4);
    threadPoolFactory.setMaxPoolSize(4);
    threadPoolFactory.setKeepAliveSeconds(0);

    CallableDecorator decorator = new CallableDecorator(){
        @Override
        public <T> Callable<T> decorate(Callable<T> task) {
            return () -> {
                try {
                    return task.call();
                }
                catch (Throwable e) {
                    synchronized (executor) {
                        if (!((MyRunnable) task).failSilent){   //note use of state of original Task
                            log.error("Execution Failure!", e);
                        }
                    }
                    throw e;
                }
            };
        }
    };
    threadPoolFactory.setCallableDecorator(decorator);
    threadPoolFactory.initialize();
    executor = threadPoolFactory.getThreadPoolExecutor();

并进一步:

    executor.submit(new MyCallable(true));

基本上,当您使用 ThreadPoolExecutor 时,有几种不同的策略与异常处理:

虽然你可以覆盖ThreadPoolExecutor.beforeExecute(...),通过反射挖出你的可运行对象,设置一个ThreadLocal,然后在afterExecute(...)中使用它,这真的感觉像是一个黑客,并且非常依赖于TPE实现。

相反,我会将您的RunnableCallable方法包装在try/catch日志错误包装器中。 这样您就可以使用以下内容向线程池中添加内容:

threadPool.submit(new RunnableWrapper(myRunnable));
// or
threadPool.submit(new CallableWrapper(myCallable));

这些将具有try/catch/log机制,并且还可以访问Runnable进行状态评估。 对我来说,其他任何事情似乎都是黑客。

您当然可以覆盖submit(...)方法来自己包装作业。 这似乎干净多了。

相关内容

  • 没有找到相关文章

最新更新