java - 即使 ScheduledFuture 被取消,ThreadPoolExecutor 也会运行

标签 java multithreading

我正在创建一个池大小未知的 ScheduledThreadPoolExecutor。池大小在运行时确定,可能在 1-5 之间,在本例中我使用大小 2。我们使用一个自定义任务,它只是每隔一段时间执行一个方法,但该方法最终会抛出一个异常(我用一个简单的 numTimes 变量和 if 语句进行了模拟)。如果抛出异常,我只想取消该特定线程的执行!如果所有线程都被取消,我想关闭ScheduledThreadPoolExecutor。一旦 numTimes == 5,我就模拟异常来取消线程),并且我可以通过多种方式设法取消线程,但它们就是感觉不对。

作为旁注,我将 ScheduledFuture 放置在各处只是为了尝试取消它。

    public class Test 
{
  static ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(2);

  public static void main(String[] args) 
  { stpe.scheduleWithFixedDelay(new UpdateTask(1), 0, 1000, TimeUnit.MILLISECONDS);
    stpe.scheduleWithFixedDelay(new UpdateTask(2), 0, 5000, TimeUnit.MILLISECONDS);
//    stpe.shutdown();
  }

  public static class UpdateTask implements Runnable
  {  
     int id;
     int numTimes = 0;
     ScheduledFuture<?> t;

     public UpdateTask(int id)
     { this.id = id;
     }

     public void run()
     { System.out.println("Hello " + id + " num: " + numTimes);
       String fn = "C:\\lib" + id;
       if (numTimes++ == 5)
       { File f = new File(fn);
         f.mkdir();
         t.cancel(false);
       }
     }
  }
}

从 run() 或 main() 调用 t.cancel() 具有相同的效果,线程停止执行,但程序不会停止运行。当然,这是因为 ThreadPoolExecutor 仍在执行任务,尽管两个线程不再被调度。

我尝试在 stpe 上调用关闭,但它没有完成线程执行。创建两个目录时将 stpe.shutdown 注释掉,否则不会。

我想不出一种优雅的方法来取消 ScheduledFuture,然后在取消所有 ScheduledFuture 时取消 ScheduledThreadPoolExecutor。

final方法##

我无法让 s1.get() 按照下面的答案中的描述工作,所以我只是创建了自己的类来处理它。

public class Test 
{
  static ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(2);
  static CancelUpdateTasks canceller;

  public static void main(String[] args) 
  { Test t = new Test();
    canceller.add(0, stpe.scheduleWithFixedDelay(new UpdateTask(0), 0, 1000, TimeUnit.MILLISECONDS));
    canceller.add(1, stpe.scheduleWithFixedDelay(new UpdateTask(1), 0, 5000, TimeUnit.MILLISECONDS));
    canceller.waitForSchedules();
    stpe.shutdown();
  }

  public Test() 
  { canceller = new CancelUpdateTasks();
  }

  public static class UpdateTask implements Runnable
  {  
     int id;
     int numTimes = 0;

     public UpdateTask(int id)
     { this.id = id;
     }

     public void run()
     { System.out.println("Hello " + id + " num: " + numTimes);
       if (numTimes++ == 5)
       { canceller.cancel(id);
       }
     }
  }

  public class CancelUpdateTasks 
  { List<ScheduledFuture<?>> scheduler;
    boolean isScheduled;

    public CancelUpdateTasks() 
    { scheduler = new ArrayList<ScheduledFuture<?>>();
      isScheduled = false;
    }

    public void waitForSchedules() 
    { int schedId = 0;
      while(isScheduled)
      { ScheduledFuture<?> schedule = scheduler.get(schedId);
        if (schedule.isCancelled())
        { if (schedId == scheduler.size() - 1)
            return;
          schedId++;
        }
        else
        { try 
          { Thread.sleep(1000);
          } 
          catch (InterruptedException e) 
          { e.printStackTrace();
          }
        }
      }
    }

    public void add(int id, ScheduledFuture<?> schedule) 
    { scheduler.add(id, schedule);
      if (!isScheduled)
        isScheduled = true;
    }

    public void cancel(int id)
    { scheduler.get(id).cancel(false);
    }

    public void cancelNow(int id)
    { scheduler.get(id).cancel(true);
    }
  }
}

最佳答案

您需要关闭池。 JVM 将继续运行,直到只有守护线程处于 Activity 状态。默认情况下,ThreadPoolExecutor 将创建非守护线程。

只需调用stpe.shutdown();

<小时/>

编辑:基于OP更新

shutdown 诚然,ScheduledThreadPoolExecutor 的关闭与普通 ThreadPoolExecutor 不同。在这种情况下,shutdown 会阻止任何计划任务重新计划。为了使其正常工作,您必须等待 future 完成。您可以通过 ScheduledFuture

上的 get() 来完成此操作
ScheduledFuture sf1 = stpe.scheduleWithFixedDelay(new UpdateTask(1), 0, 1000, TimeUnit.MILLISECONDS);
ScheduledFuture sf2 = stpe.scheduleWithFixedDelay(new UpdateTask(2), 0, 5000, TimeUnit.MILLISECONDS);
sf1.get();
sf2.get();
stpe.shutdown();

在这种情况下,两个任务都是异步运行的,主线程将首先等待 sf1 完成,然后等待 sf2 完成,最后关闭。

关于java - 即使 ScheduledFuture 被取消,ThreadPoolExecutor 也会运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20977932/

相关文章:

java - 尝试检索字段值时出现 NoSuchFieldException

java - 我如何重用我的 JFrame 来一个接一个地显示多个 GUI,而不是为每个创建一个新的 JFrame?

java - Spring Data - 从表中获取最后一条记录

java - 选择 ListView 项目后应用程序自动关闭

android - 在单独的线程中使用 while 循环生成 android 按钮

multithreading - Haskell 中的生产者和消费者问题?

java - ExecutorService 终止,但 JVM 没有

java - JTextArea 不断将主面板滚动到顶部

c++ - boost::线程函数执行

c# - 在 C# .NET 标准中,为什么使用位测试 "Running"确定线程状态 `(ThreadState & (Stopped | Unstarted)) == 0`