简体   繁体   English

ExecutorService在提交新任务时取消当前任务

[英]ExecutorService that cancels current task when new ones are submitted

Consider a user interface that accepts configuration settings for a service that takes a long time to initialize (for example, the parameters for a JDBC connection). 考虑一个用户接口,该接口接受需要很长时间初始化的服务的配置设置(例如,JDBC连接的参数)。 We want our user interface to remain responsive while the service initialization occurs. 我们希望我们的用户界面在服务初始化时保持响应。 If the user makes additional changes, the initialization should be cancelled and restarted using the new parameters. 如果用户进行了其他更改,则应取消初始化并使用新参数重新启动。

Because parameters are incorporated in the configuration as the user types each character, it's possible that a number of initialization requests could be created in a row. 由于参数在用户键入每个字符时包含在配置中,因此可能会在一行中创建许多初始化请求。 Only the last one should be executed. 只应该执行最后一个。

We have code that we've put together that achieves this result, but it seems like this behavior is a very good candidate for implementation as an ExecutorService. 我们已经将代码放在一起实现了这个结果,但看起来这个行为非常适合作为ExecutorService实现。 Before we refactor everything into an ExecutorService, I figured I would ask if there are similar implementations already out in the world. 在我们将所有内容重构为ExecutorService之前,我想我会问世界上是否有类似的实现。

To be more specific: 更具体:

The ExecutorService would have one worker thread. ExecutorService将有一个工作线程。 As soon as a new task is submitted, the current task is cancelled (and the worker interrupted). 提交新任务后,当前任务将被取消(工作人员将被中断)。 The new task is then captured for the next execution. 然后捕获新任务以供下次执行。 If another task is submitted, the current task is cancelled again, and the "next execution" task is set to this new task. 如果提交了另一个任务,则再次取消当前任务,并将“下次执行”任务设置为此新任务。 When the worker thread finally picks up the next task for execution, it will always be the last task submitted - all other tasks are either cancelled or discarded. 当工作线程最终选择执行下一个任务时,它将始终是最后一个提交的任务 - 所有其他任务都被取消或被丢弃。

Does anyone have an implementation like that they'd be willing to share? 有没有人有他们愿意分享的实施? Or is there maybe a standard library that covers this type of behavior? 或者是否有可能涵盖此类行为的标准库? It's not hard to implement, but getting the thread safety nailed down can be tricky, so I'd rather use proven code if we can. 这并不难实现,但是确保线程安全可靠是非常棘手的,所以如果可以的话,我宁愿使用经过验证的代码。

Here's what I eventually came up with - I'm interested in any comments: 这是我最终提出的 - 我对任何评论感兴趣:

public class InterruptingExecutorService extends ThreadPoolExecutor{
    private volatile FutureTask<?> currentFuture;

    public InterruptingExecutorService(boolean daemon) {
        super(0, 1, 1000L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(), 
                daemon ? new DaemonThreadFactory() : Executors.defaultThreadFactory());

    }

    public static class DaemonThreadFactory implements ThreadFactory{
        ThreadFactory delegate = Executors.defaultThreadFactory();

        @Override
        public Thread newThread(Runnable r) {
            Thread t = delegate.newThread(r);
            t.setDaemon(true);
            return t;
        }

    }

    private void cancelCurrentFuture(){
        // cancel all pending tasks
        Iterator<Runnable> it = getQueue().iterator();
        while(it.hasNext()){
            FutureTask<?> task = (FutureTask<?>)it.next();
            task.cancel(true);
            it.remove();
        }

        // cancel the current task
        FutureTask<?> currentFuture = this.currentFuture;
        if(currentFuture != null){
            currentFuture.cancel(true);
        }
    }

    @Override
    public void execute(Runnable command) {
        if (command == null) throw new NullPointerException();

        cancelCurrentFuture();
        if (!(command instanceof FutureTask)){ // we have to be able to cancel a task, so we have to wrap any non Future
            command = newTaskFor(command, null);
        }
        super.execute(command);
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        // it is safe to access currentFuture like this b/c we have limited the # of worker threads to only 1
        // it isn't possible for currentFuture to be set by any other thread than the one calling this method
        this.currentFuture = (FutureTask<?>)r;
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        // it is safe to access currentFuture like this b/c we have limited the # of worker threads to only 1
        // it isn't possible for currentFuture to be set by any other thread than the one calling this method
        this.currentFuture = null;
    }
}

you may need add a DiscardOldestPolicy to your executor 您可能需要向执行程序添加DiscardOldestPolicy

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ThreadPoolExecutor.DiscardOldestPolicy.html http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ThreadPoolExecutor.DiscardOldestPolicy.html

You will get
0 submitted
1 submitted
2 submitted
3 submitted
4 submitted
5 submitted
6 submitted
7 submitted
8 submitted
9 submitted
9 finished

public static void main(String[] args) throws SecurityException,
        NoSuchMethodException {

    final Method interruptWorkers = ThreadPoolExecutor.class
            .getDeclaredMethod("interruptWorkers");
    interruptWorkers.setAccessible(true);

    ExecutorService executor = new ThreadPoolExecutor(1, 1, 0L,
            TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(),
            new RejectedExecutionHandler() {

                @Override
                public void rejectedExecution(Runnable r,
                        ThreadPoolExecutor executor) {
                    if (!executor.isShutdown()) {
                        try {

                            interruptWorkers.invoke(executor);
                            executor.execute(r);

                        } catch (IllegalArgumentException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });


    for(int i =0 ;i<10;i++)
        executor.submit(newTask(i));
}

private static Runnable newTask(final int id) {
    return new Runnable() {
        {

            System.out.println(id + " submitted");
        }

        @Override
        public void run() {
            try {

                Thread.sleep(5000l);
                System.out.println(id + " finished");
            } catch (InterruptedException e) {
            }

        }

    };
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 ExecutorService 为每个提交的任务指定使用新线程 - ExecutorService specify use new thread for each submitted task 在给定时间内没有提交任务时关闭 ExecutorService - Shutdown ExecutorService when no task has been submitted for a given time 提交给 ExecutorService 对象的任务的执行顺序是什么? - What is the order of execution for submitted task to the ExecutorService object? 为什么ExecutorService.awaitTermination()在提交的任务完成之前成功 - Why is ExecutorService.awaitTermination() succeeding before a submitted task has completed ExecutorService并在关闭后等待其他任务完成时接受新任务 - ExecutorService and accepting new task after shutdown when waiting for other task to finish ExecutorService永远不会停止。在另一个执行任务中执行新任务时 - ExecutorService never stops. When execute new task inside another executing task 是否有一个 ExecutorService 为每个任务创建一个新线程? - Is there an ExecutorService that creates a new thread for every task? 停止提交给ExecutorService的Runnable - Stop a Runnable submitted to ExecutorService 如何在提交给ExecutorService的Task中处理第三方Java代码,以防它具有无限循环 - How to handle third-party Java code in a Task submitted to ExecutorService in case it has an infinite loop 如何单元测试ExecutorService为任务生成新线程? - How to unit test that ExecutorService spawns new thread for task?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM