Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/345.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 当ExecutorService上的所有任务完成或取消时,为什么不可靠地返回_Java_Multithreading_Executorservice_Executor - Fatal编程技术网

Java 当ExecutorService上的所有任务完成或取消时,为什么不可靠地返回

Java 当ExecutorService上的所有任务完成或取消时,为什么不可靠地返回,java,multithreading,executorservice,executor,Java,Multithreading,Executorservice,Executor,我是我的代码,我向ExecutorService提交一些任务,然后使用shutdown()和waitTermination()等待它们完成。但是,如果任何一项任务需要更长的时间才能完成,我希望在不影响其他任务的情况下取消它。我使用的代码修订代码如下: package com.jthink.jaikoz.memory; import com.jthink.jaikoz.MainWindow; import java.util.List; import java.util.concurrent.

我是我的代码,我向ExecutorService提交一些任务,然后使用shutdown()和waitTermination()等待它们完成。但是,如果任何一项任务需要更长的时间才能完成,我希望在不影响其他任务的情况下取消它。我使用的代码修订代码如下:

package com.jthink.jaikoz.memory;

import com.jthink.jaikoz.MainWindow;

import java.util.List;
import java.util.concurrent.*;

public class TimeoutThreadPoolExecutor extends ThreadPoolExecutor {
    private final long timeout;
    private final TimeUnit timeoutUnit;

    private boolean isShutdown = false;

    private final ScheduledExecutorService timeoutExecutor = Executors.newSingleThreadScheduledExecutor();

    //Map Task to the Timeout Task that could be used to interrupt it
    private final ConcurrentMap<Runnable, ScheduledFuture> runningTasks = new ConcurrentHashMap<Runnable, ScheduledFuture>();

    public long getTimeout()
    {
        return timeout;
    }

    public TimeUnit getTimeoutUnit()
    {
        return timeoutUnit;
    }

    public TimeoutThreadPoolExecutor(int workerSize, ThreadFactory threadFactory, long timeout, TimeUnit timeoutUnit)
    {
        super(workerSize, workerSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory);
        this.timeout = timeout;
        this.timeoutUnit = timeoutUnit;
    }

    public TimeoutThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, long timeout, TimeUnit timeoutUnit) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        this.timeout = timeout;
        this.timeoutUnit = timeoutUnit;
    }

    public TimeoutThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, long timeout, TimeUnit timeoutUnit) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
        this.timeout = timeout;
        this.timeoutUnit = timeoutUnit;
    }

    public TimeoutThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler, long timeout, TimeUnit timeoutUnit) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
        this.timeout = timeout;
        this.timeoutUnit = timeoutUnit;
    }

    public TimeoutThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler, long timeout, TimeUnit timeoutUnit) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
        this.timeout = timeout;
        this.timeoutUnit = timeoutUnit;
    }

    @Override
    public void shutdown() {
        isShutdown = true;
        super.shutdown();
    }

    @Override
    public List<Runnable> shutdownNow() {
        timeoutExecutor.shutdownNow();
        return super.shutdownNow();
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        if(timeout > 0) {
            //Schedule a task to interrupt the thread that is running the task after time timeout
            final ScheduledFuture<?> scheduled = timeoutExecutor.schedule(new TimeoutTask(t), timeout, timeoutUnit);

            //Add Mapping
            runningTasks.put(r, scheduled);
        }
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {

        //Remove mapping and cancel timeout task
        ScheduledFuture timeoutTask = runningTasks.remove(r);
        if(timeoutTask != null) {
            timeoutTask.cancel(false);
        }

        if (isShutdown)
        {
            if(getQueue().isEmpty())
            {
                //Queue is empty so all tasks either finished or currently running
                MainWindow.logger.severe("---Thread Pool Queue is Empty");
                timeoutExecutor.shutdown();
            }
        }
    }

    /**
     * Interrupt the thread
     *
     */
    class TimeoutTask implements Runnable {
        private final Thread thread;

        public TimeoutTask(Thread thread) {
            this.thread = thread;
        }

        @Override
        public void run() {
            MainWindow.logger.severe("Cancelling task because taking too long");
            thread.interrupt();
        }
    }
}
is output WAITTERMINATION()不会返回,只有在用户两小时后取消任务时才会最终返回-此处为完整日志提取

14/12/2014 20.44.19:com.jthink.jaikoz.manipulate.CorrectFromMusicBrainzWorker:getSongsNotMatched:SEVERE: /Volumes/2TB External/New iTunes Library/iTunes Media/Music/XTC:albumMetadataMatchingCounts11:AlreadyMatched:2:ToMatch:11
14/12/2014 20.44.19:com.jthink.jaikoz.memory.TimeoutThreadPoolExecutor:afterExecute:SEVERE: ---Thread Pool Queue is Empty
14/12/2014 22.18.01:com.jthink.jaikoz.manipulate.ExecutorServiceEnabledAnalyser:cancelTask:WARNING: Cancelling class com.jthink.jaikoz.manipulate.CorrectFromMusicBrainzAnalyser Task
14/12/2014 22.18.01:com.jthink.jaikoz.manipulate.CorrectFromMusicBrainzAnalyser:matchToRelease:WARNING: class com.jthink.jaikoz.manipulate.CorrectFromMusicBrainzAnalyser has been interrupted
那么,即使日志显示队列为空,并且因此对执行器本身和嵌入的timeoutExecutor都调用了shutdown(),WaitTermination()怎么可能不返回呢

我自己对此也有一些想法,但不知道答案

  • 首先,为什么实际上需要关闭TimeOutExecutor以使WaitTermination()返回。在我的子类中,awaitTermination()未被重写,因此如果所有任务都已完成,那么TiumOutExecutor(awaitTermination()不知道是否关闭)又有什么关系

  • 第二,为什么---线程池队列是空的,有时会多次获得输出

  • TimeOutExecutor是单线程的,是否正确/必要

  • 基于Holgers答案的更新

    因此,您的问题是您正在关闭 timeoutExecutor太早,因此它可能会错过一个或多个 中断线程池执行器的挂起任务的任务

    现在我明白了,空队列只意味着所有任务都已完成或启动。(很抱歉,我的示例测试之前有误导性,它运行的任务超过10个,这是临时编辑,在生产代码中,工作人员的数量基于计算机用户上的CPU数量)

    你是说我过早地关闭了timeoutExecutor(可能有多达WorkerSize-1个任务仍在运行),这意味着所有仍在为尚未完成的任务运行的timeoutExecutor都会被中断。因此,如果剩下的任何一个由于某种原因不能自动完成,那么他们的超时任务将不再存在,因此不能用来中断他们。但是等待Termination()woiuldnt返回的唯一原因是如果最后一个(WorkerSize-1)任务之一没有完成

    我自愿地改变了以前的生活

    为了确保它能够完成,我使用了shutdownNow(),但直到一切都完成,但根据您的评论,这可能仍然不起作用

    我应该这样做

    protected void afterExecute(Runnable r, Throwable t) {
        ScheduledFuture timeoutTask = runningTasks.remove(r);
        if(timeoutTask != null) {
            timeoutTask.cancel(false);
        }
    }
    

    一旦提交的所有任务完成(自然完成或通过被相应的timeoutExecutor取消),就会调用terminated()。此时timeoutExecutor是否仍然存在并不重要

    对于completness,修改我的测试用例,以便任务将花费很长时间,除非超时任务正在工作,否则显示原始解决方案失败(挂起),并且修改后的解决方案正在工作

    public void testThreadPoolTasksCancelled() throws Exception
        {
            Instant t1, t2;
            t1 = Instant.now();
            final TimeoutThreadPoolExecutor executorService = new TimeoutThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), 3, TimeUnit.SECONDS);
    
            for (int i = 0; i < 50; i++)
            {
                executorService.submit(new Callable<Object>()
                {
                    @Override
                    public Object call() throws Exception
                    {
                        Thread.sleep(500000000);
                        System.out.println("Done");
                        return null;
                    }
    
                });
            }
            executorService.shutdown();
            executorService.awaitTermination(1, TimeUnit.DAYS);
            t2 = Instant.now();
            System.out.println("Program done:"+(Duration.between(t1, t2).toMillis()/ 1000+ " seconds"));
        }
    
    public void testThreadPoolTasksCancelled()引发异常
    {
    瞬间t1,t2;
    t1=瞬间。现在();
    final TimeoutThreadPoolExecutor Executor Service=新的TimeoutThreadPoolExecutor(10,10,0L,TimeUnit.毫秒,新的LinkedBlockingQueue(),3,TimeUnit.秒);
    对于(int i=0;i<50;i++)
    {
    executorService.submit(新的可调用()
    {
    @凌驾
    公共对象调用()引发异常
    {
    线程。睡眠(500000000);
    系统输出打印项次(“完成”);
    返回null;
    }
    });
    }
    executorService.shutdown();
    执行人服务。等待终止(1,时间单位。天);
    t2=瞬间。现在();
    System.out.println(“程序完成:”+(Duration.between(t1,t2.toMillis()/1000+“秒”);
    }
    
    队列仅包含尚未启动的作业。空队列并不意味着没有挂起的作业;他们可能只是为了执行而被移除。特别是在您的示例代码中,假设空队列意味着没有正在运行的作业是完全错误的;由于您将executor配置为具有十个核心线程并提交十个作业,因此在示例代码的整个执行过程中,队列始终为空

    因此,您的问题是过早地关闭了
    timeoutExecutor
    ,因此它可能会错过一个或多个任务来中断线程池执行器的挂起任务

    请注意,原则上,作业甚至可能处于从队列中删除(如果有添加)的状态,但尚未调用
    beforecute
    。因此,即使有一个空队列和一个空的
    runningTasks
    map也不能保证没有挂起的作业


    要回答另一个问题,您必须关闭
    timeoutExecutor
    ,因为它有一个关联的活动线程,该线程将始终保持执行器的活动状态。因此,不关闭它将导致内存泄漏,并进一步保持线程的活动状态,因此始终防止JVM自动关闭

    但是关闭
    timeoutExecutor
    的正确位置是重写方法
    protectedvoid terminated()
    ,该方法正好用于清理



    最后一点,不管您的
    timeoutExecutor
    有多少线程,但考虑到任务有多简单,拥有多个线程没有任何好处,而单线程执行器是最简单、可能也是最有效的解决方案。

    感谢您的精彩回答我根据您所说的内容更新了我的问题,如果您同意,我将不胜感激。我刚刚意识到传递给beforeExecute的Runnable实际上不是提交的ta
    14/12/2014 20.44.19:com.jthink.jaikoz.manipulate.CorrectFromMusicBrainzWorker:getSongsNotMatched:SEVERE: /Volumes/2TB External/New iTunes Library/iTunes Media/Music/XTC:albumMetadataMatchingCounts11:AlreadyMatched:2:ToMatch:11
    14/12/2014 20.44.19:com.jthink.jaikoz.memory.TimeoutThreadPoolExecutor:afterExecute:SEVERE: ---Thread Pool Queue is Empty
    14/12/2014 22.18.01:com.jthink.jaikoz.manipulate.ExecutorServiceEnabledAnalyser:cancelTask:WARNING: Cancelling class com.jthink.jaikoz.manipulate.CorrectFromMusicBrainzAnalyser Task
    14/12/2014 22.18.01:com.jthink.jaikoz.manipulate.CorrectFromMusicBrainzAnalyser:matchToRelease:WARNING: class com.jthink.jaikoz.manipulate.CorrectFromMusicBrainzAnalyser has been interrupted
    
    protected void afterExecute(Runnable r, Throwable t) {
        ScheduledFuture timeoutTask = runningTasks.remove(r);
        if(timeoutTask != null) {
            timeoutTask.cancel(false);
        }
        if (isShutdown)
        {
            if(getQueue().isEmpty())
            {
    
                if(runningTasks.size()==0)
                {
                    this.shutdownNow();
                }
            }
        }
    }
    
    protected void afterExecute(Runnable r, Throwable t) {
        ScheduledFuture timeoutTask = runningTasks.remove(r);
        if(timeoutTask != null) {
            timeoutTask.cancel(false);
        }
    }
    
    protected void terminated() 
    {
        timeoutExecutor.shutdown();
    }
    
    public void testThreadPoolTasksCancelled() throws Exception
        {
            Instant t1, t2;
            t1 = Instant.now();
            final TimeoutThreadPoolExecutor executorService = new TimeoutThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), 3, TimeUnit.SECONDS);
    
            for (int i = 0; i < 50; i++)
            {
                executorService.submit(new Callable<Object>()
                {
                    @Override
                    public Object call() throws Exception
                    {
                        Thread.sleep(500000000);
                        System.out.println("Done");
                        return null;
                    }
    
                });
            }
            executorService.shutdown();
            executorService.awaitTermination(1, TimeUnit.DAYS);
            t2 = Instant.now();
            System.out.println("Program done:"+(Duration.between(t1, t2).toMillis()/ 1000+ " seconds"));
        }