如何扩展FutureTask并确保发布对Callable的引用?

时间:2015-01-08 12:25:49

标签: java executorservice scheduledexecutorservice

我有自定义ExecutorService,其中包含一个ScheduledExecutorService,如果它们花费的时间太长,可以用来中断提交给ExecutorSerice的任务,我把complet类放在这篇文章的末尾。

这工作正常,但有时中断本身就会引起问题,所以我将一个volatile布尔 cancel 标志添加到一个新的CanceableTask类中并使它们成为子类,以便它们可以检查如果布尔值已经发送到真,那么就干净地停下来。请注意,它们是提交给执行程序服务precisley的每个类中的boolean实例,以便可以在不取消其他任务的情况下取消长时间运行的任务。

然而 FutureTask 作为参数传递给 beforeExecute(Thread t,Runnable r),这不能访问Callable类,所以我的Timeout代码不能设置取消标志。

通过覆盖 newTaskFor 方法返回一个只提供Callable

的引用的类来解决这个问题。
public class FutureCallable<V> extends FutureTask<V>
{
    private Callable<V> callable;
    public FutureCallable(Callable<V> callable) {
        super(callable);
        this.callable = callable;
    }
    public Callable<V> getCallable() {
        return callable;
    }
}

并且一切都很好,或者我认为。

不幸的是,我的应用程序现在使用越来越多的内存,因为新的任务被提交到ExecutorService并最终耗尽内存,当我分析应用程序时,我发现有一个Thread堆栈本地引用所有的FutureCallables,即使之后任务已经完成,因为FutureCallable引用了正在运行的类,它使用了很多内存。

当我查看FutureTask的代码(FutureCallable扩展)时,对于私有Callable引用有一个注释

/** The underlying callable; nulled out after running */

那么我怎么能改进我的FutureCallable来取消它对Callable的引用呢? 或者为什么在任务完成后仍然存在对FutureCallable的引用。

我已经确认如果我注释掉newTaskFor方法没有过多的内存使用,但遗憾的是我无法取消该类。

完整课程

公共类TimeoutThreadPoolExecutor扩展ThreadPoolExecutor {     私人决赛长时间超时;     private final TimeUnit timeoutUnit;

private final static int WAIT_BEFORE_INTERRUPT = 10000;
private final static int WAIT_BEFORE_STOP      = 10000;


private final ScheduledExecutorService timeoutExecutor = Executors.newSingleThreadScheduledExecutor();

//Map Task to the Future of the Timeout Task that could be used to interrupt it
private final ConcurrentMap<Runnable, ScheduledFuture> runningTasks = new ConcurrentHashMap<Runnable, ScheduledFuture>();

public long getTimeout()
{
    return timeout;
}

public TimeUnit getTimeoutUnit()
{
    return timeoutUnit;
}

public TimeoutThreadPoolExecutor(int workerSize, ThreadFactory threadFactory, long timeout, TimeUnit timeoutUnit)
{
    super(workerSize, workerSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory);
    MainWindow.logger.severe("Init:"+workerSize+":Timeout:"+timeout+":"+timeoutUnit);
    this.timeout = timeout;
    this.timeoutUnit = timeoutUnit;
}

public TimeoutThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, long timeout, TimeUnit timeoutUnit) {
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    this.timeout = timeout;
    this.timeoutUnit = timeoutUnit;
}

@Override
public <T> FutureCallable<T> newTaskFor(Callable<T> callable) {
    return new FutureCallable<T>(callable);
}

@Override
public List<Runnable> shutdownNow() {
    timeoutExecutor.shutdownNow();
    return super.shutdownNow();
}

@Override
protected void beforeExecute(Thread t, Runnable r) {
    if(timeout > 0) {
        //Schedule a task to interrupt the thread that is running the task after time timeout starting from now
        final ScheduledFuture<?> scheduled = timeoutExecutor.schedule(new TimeoutTask(t, r), timeout, timeoutUnit);

        //Add Mapping
        runningTasks.put(r, scheduled);
    }
}

@Override
protected void afterExecute(Runnable r, Throwable t) {

    //AfterExecute will be called after the task has completed, either of its own accord or because it
    //took too long and was interrupted by corresponding timeout task
    //Remove mapping and cancel timeout task
    ScheduledFuture timeoutTask = runningTasks.remove(r);
    if(timeoutTask != null) {
        timeoutTask.cancel(false);
    }

}

@Override
protected void terminated()
{
    //All tasks have completed either naturally or via being cancelled by timeout task so close the timeout task
    MainWindow.logger.severe("---Shutdown TimeoutExecutor");
    timeoutExecutor.shutdown();
}

/**
 * Interrupt or possibly stop the thread
 *
 */
class TimeoutTask implements Runnable {
    private final       Thread thread;
    private             Callable c;

    public TimeoutTask(Thread thread, Runnable c) {
        this.thread = thread;
        if(c instanceof FutureCallable)
        {
            this.c = ((FutureCallable) c).getCallable();
        }
    }

    @Override
    public void run()
    {
        String msg = "";
        if (c != null)
        {
            if (c != null && c instanceof CancelableTask)
            {
                MainWindow.logger.severe("+++Cancelling " + msg + " task because taking too long");
                ((CancelableTask) c).setCancelTask(true);
            }
        }
    }
}

}

public abstract class CancelableTask  extends ExecutorServiceEnabledAnalyser
{
    private volatile boolean cancelTask = false;

    public boolean isCancelTask() {
        return cancelTask;
    }

    public void setCancelTask(boolean cancelTask) {
        this.cancelTask = cancelTask;
    }

    CancelableTask(final MainWindow start, boolean isSelectedRecords, boolean isUseRowSelection)
    {
        super(start, isSelectedRecords, isUseRowSelection);
    }

    CancelableTask(final MainWindow start, List<MetadataChangedWrapper> songs)
    {
        super(start, songs );
    }

}

1 个答案:

答案 0 :(得分:0)

ThreadLocal在哪里?我觉得很奇怪,很难相信你在说什么,即使在完成之后,它仍然会引用所有正在运行的任务。如果是这种情况,即使没有覆盖,它也应该耗尽内存(任务本身虽然使用了一些内存,但可能小于你的可调用内存,但仍然不是零)。

无论如何,您可以覆盖done上的FutureCallable方法,以便在执行后将包装对象清空。