[英]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.