考虑一个用户界面,该界面接受需要很长时间初始化的服务的配置设置(例如,JDBC连接的参数)。我们希望我们的用户界面在服务初始化时保持响应。如果用户进行了其他更改,则应使用新参数取消并重新启动初始化。
由于参数在用户键入每个字符时包含在配置中,因此可能会在一行中创建许多初始化请求。只应执行最后一个。
我们已经将代码放在一起实现了这个结果,但似乎这个行为非常适合作为ExecutorService实现。在我们将所有内容重构为ExecutorService之前,我想我会问世界上是否已经有类似的实现。
更具体一点:
ExecutorService将有一个工作线程。提交新任务后,当前任务将被取消(工作人员将被中断)。然后捕获新任务以供下次执行。如果提交了另一个任务,则再次取消当前任务,并将“下次执行”任务设置为此新任务。当工作线程最终选择执行下一个任务时,它将始终是最后一个提交的任务 - 所有其他任务都被取消或丢弃。
有没有人有他们愿意分享的实施?或者是否有可能涵盖此类行为的标准库?这并不难实现,但是确保线程安全可靠是非常棘手的,所以如果可以的话,我宁愿使用经过验证的代码。
答案 0 :(得分:3)
这是我最终想出的 - 我对任何评论感兴趣:
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;
}
}
答案 1 :(得分:1)
您可能需要向执行者添加DiscardOldestPolicy
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) {
}
}
};
}