java - Java 中只有最近的任务 ExecutorService
问题描述
我想ExecutorService
在Java中创建一个,当给定一个任务时,它会停止并丢弃它的当前任务(如果有当前任务)并执行给定的任务。当给ExecutorService
它一个新任务时,总是因为以前的任务变得无关紧要,不再值得执行。
Java中是否有内置方法可以做到这一点,还是我应该自己实现这种行为?还是在这种情况下有另一种更好的方法?
解决方案
这是一个有趣的问题。我对核心ExecutorService
实现有了更深入的了解。谢谢!
解决没有ExecutorService
根据您所提到的,您将有至多一个线程执行任务和至多一个待处理的任务,因为我们只对最后提交的任务感兴趣。你真的需要一个ExecutorService
吗?
您可以只在POJO 对象的字段中保存下一个任务。static
AtomicReference
由于我们只对最新的任务感兴趣,因此任务生产者可以简单地替换AtomicReference
. 当前任务执行完成后,任务使用者就可以从该字段中获取。该字段必须是:
static
因为该字段应该只有一个实例AtomicReference
因为多个线程可能正在尝试设置下一个任务。
解决使用ExecutorService
但是,如果您仍然想走这ExecutorService
条路,您可以尝试一下。创建一个ThreadPoolExecutor
只有一个线程(核心和最大值)的线程,并给它一个BlockingQueue
实现,一旦添加了一个新元素,它就会“忘记”它的所有元素。
这是一个提交新任务 100 次的示例测试代码。如果之前的任务还没有被占用执行,则将其丢弃。如果是,则执行它并将新的排队。
public class OnlyOneTask{
public static void main( String[] args ){
ExecutorService svc = null;
/* A BlockingQueue that immediately "forgets" all tasks it had as soon as a new one is "offered". */
BlockingQueue<Runnable> Q = new ArrayBlockingQueue<Runnable>( 1 ) {
private static final long serialVersionUID = 1L;
/* Forget the current task(s) and add the new one
* TODO These 2 steps may need synchronization. */
public boolean offer( Runnable e) {
clear();
return super.offer( e );
}
};
try {
/* A ThreadPoolExecutor that uses the queue we created above. */
svc = new ThreadPoolExecutor( 1, 1, 5000, TimeUnit.MILLISECONDS, Q );
for( int i = 0; i < 100; i++ ) {
/* Our simple task. */
int id = i;
Runnable r = () -> {
System.out.print( "|" + id + "|" );
};
svc.submit( r );
/* A delay generator. Otherwise, tasks will be cleared too fast. */
System.out.print( " " );
}
}
finally {
svc.shutdown();
try{
svc.awaitTermination( 10, TimeUnit.SECONDS );
}
catch( InterruptedException e ){
e.printStackTrace();
}
}
}
}
这个示例课程只是为了让您了解我认为可行的方法。您肯定需要改进此实现中的以下缺点:
- 第一个任务无论如何都会被执行,因为它会立即被
ExecutorService
. (这就是为什么下一点变得重要的原因。) - 如有必要,必须将可中断性/可取消性引入正在运行的任务中
另一种使用ExecutorService
和Future.cancel()
如果您正在检查任务中的线程中断,这实际上是最简单的。这与上面基本相同,但我们只是简单地使用来表示我们不需要执行最后一个任务,而不是clear()
ing 队列。Future.cancel()
public static void main( String[] args ){
ExecutorService svc = null;
try {
/* A single thread executor is enough. */
svc = Executors.newSingleThreadExecutor();
Future<?> f = null;
for( int i = 0; i < 100; i++ ) {
int id = i;
/* Our simple task. */
Runnable r = () -> {
/* If the thread has been interrupted (by the Future.cancel() call, then return from here. */
if( Thread.currentThread().isInterrupted() ) return;
System.out.print( "|" + id + "|" );
};
if( f != null ) f.cancel( true );
f = svc.submit( r );
/* A pseudo delay generator. */
System.out.print( " " );
}
}
finally {
svc.shutdown();
try{
svc.awaitTermination( 10, TimeUnit.SECONDS );
}
catch( InterruptedException e ){
e.printStackTrace();
}
}
}