在ScheduledThreadPoolExecutor中启动任务早期/跳转启动

时间:2016-02-01 10:45:09

标签: java multithreading threadpool java.util.concurrent scheduledexecutorservice

我有一个执行器服务,它定期执行一堆任务。它们在启动时初始化并且经常运行,到目前为止一直很好。

我现在想添加一些功能,以根据事件快速启动这些任务的执行。

我找到了decorateTask方法,它允许我存储我安排的任务。但是,我不确定如何让它们运行?

我有想法在RunnableScheduledFuture中覆盖Delayed方法让它在预定义的事件上返回0,但是我也不确定这是否可行以及如果我这样做会执行者的行为如何?

另一个想法是收集所有任务,然后直接在事件上提交它们。也不确定这会如何表现。

我不能只调用它们,因为它们将在同一个线程中运行。

我希望这一切都有道理。如果有什么不清楚,请告诉我。

public class EventBasedExecutor extends ScheduledThreadPoolExecutor implements EventBasedExecutorService {
private static final Logger log = Logger.getLogger(EventBasedExecutor.class);

private List<RunnableScheduledFuture<?>> workers = new ArrayList<>();

public EventBasedExecutor(int corePoolSize, ThreadFactory threadFactory) {
    super(corePoolSize, threadFactory);

}

@Override
protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) {
    workers.add(task);
    return super.decorateTask(runnable, task);
}

@Override
public void executeEarly() {
        // do something here to start the executors work  
}

2 个答案:

答案 0 :(得分:1)

最简洁易懂的方法是收集所有任务并直接在事件中运行。我只需要使用ExecutorService的invokeAll。以下代码示例可以提供帮助:

public void handleSomeEvent(Event event) {
    List<Task> tasksToRunOnEvent = getTasksToRunOnEvent(event);
    List<Future<TaskResult> futures = executorService.invokeAll(tasksToRunOnEvent);
    handleTaskResults(futures);
}

答案 1 :(得分:0)

我接受了tvelykyy的建议并玩了一下,并想出了这个意思:

public class EventBasedExecutor extends ScheduledThreadPoolExecutor implements EventBasedExecutorService {

    private List<RunnableScheduledFuture<?>> workers = new ArrayList<>();

    private int index;

    public EventBasedExecutor(int corePoolSize) {
        super(corePoolSize,  new ThreadFactoryBuilder().setDaemon(true).setNameFormat("message-sender-%d").build());
    }

    @Override
    protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) {
        if(!workers.contains(runnable)) {
            workers.add(task);
        }
        return super.decorateTask(runnable, task);
    }

    @Override
    public void executeEarly() {
        if(index >= workers.size()) {
            index = 0;
        }

        if(workers.size() == 0) {
            return;
        }

        RunnableScheduledFuture<?> runnableScheduledFuture = workers.get(index);
        index ++;
        execute(runnableScheduledFuture);
    }

}

几句话:

workers.contains(Runnable)是真的,如果你重新提交一个RunnableScheduledFuture。

你必须通过execute提交它才能让它在线程池中运行(而不是只在runnable上调用run来阻止当前线程)。

我建议使用runnables来实现一个安全防护装置,这样它们就不能同时执行两次(对我的用例很重要)。所以基本上:

if( isrunning() ) return;

在decorateTask中 - 你必须重新装饰runnable。这是因为如果必须的话,实现将采用包装的runnable并重新安排它。由于旧的runnable已经安排好,你不想重新安排它们。所以调用execute将遍历装饰并装饰预定的runnable(通常每秒或一些运行)作为不可重复的runnable。这意味着,您的计划任务可以在不重新计划的情况下运行一次,同时仍可作为执行程序中的定期运行。

我希望这一切都有道理:)

阿图尔