首页 > 解决方案 > Java 中只有最近的任务 ExecutorService

问题描述

我想ExecutorService在Java中创建一个,当给定一个任务时,它会停止并丢弃它的当前任务(如果有当前任务)并执行给定的任务。当给ExecutorService它一个新任务时,总是因为以前的任务变得无关紧要,不再值得执行。

Java中是否有内置方法可以做到这一点,还是我应该自己实现这种行为?还是在这种情况下有另一种更好的方法?

标签: 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. (这就是为什么下一点变得重要的原因。)
  • 如有必要,必须将可中断性/可取消性引入正在运行的任务中

另一种使用ExecutorServiceFuture.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();
        }
    }
}

推荐阅读