【问题标题】:can't stop a Runnable that passing itself in ScheduledExecutorService.schedule无法停止在 ScheduledExecutorService.schedule 中传递自身的 Runnable
【发布时间】:2012-01-19 08:48:21
【问题描述】:

我有一个从 Runnable 实现的轮询器。轮询器将自身传递给 ScheduledExecutorService。由于我需要根据任务不同的延迟时间,所以我使用'schedule'而不是'scheduleWithFixedDelay',这有问题......

很遗憾,此轮询器无法正常关闭...

日志显示: 一开始,“主”线程调度一个轮询器; 之后,“poller18”(或任何线程 ID)线程调度以下轮询器。

当被摧毁时, 'main'线程调用destroy,设置flag为true,然后调用poller.shutdown; 但是 'poller18' 线程永远不会看到该标志(在 schedulePoller 中始终为 false),因此它将继续调度下一个轮询器。

我的问题是: 1. isShuttingDown 是类中的私有字段。我认为它将由线程共享,因为它不是 ThreadLocal。为什么不是? 2.在这种情况下,有没有其他方法可以通知poller18 poller正在关闭?

class Poller implements Runnable {
    private boolean isShuttingDown = false;
    private ScheduledExecutorService poller = null;

    @PostConstruct
    protected synchronized void start() {
      if (enabled && poller == null) {
          poller = Executors.newScheduledThreadPool(1);
          schedulePoller(1);
      }
   }

   protected synchronized void schedulePoller(final long period) {
      if (poller() == null || poller().isShutdown() || this.isShuttingDown) {
          return;
      }
      LOGGER.info("schedule a new poller");
      poller().schedule(this,
              period,
              TimeUnit.MILLISECONDS);
   }

   public void run() {
      ... do work ...
      if (more work)
           schedulePoller(1);
      else
           schedulePoller(10);
   }

   public void destroy() {
      this.isShuttingDown = true;
      poller.shutdown();
      while (!poller.awaitTermination(SHUTDOWN_WAIT_SECONDS, TimeUnit.SECONDS)) {
          LOGGER.info("Waiting for remaining tasks to finish.");
          poller.shutdown();
      }
      LOGGER.info("All remaining tasks have finished.");
   }
}

在 spring 配置中,我将 destroy_method 设置为 destroy()。

非常感谢!如果我的描述中有任何混淆,请告诉我。

【问题讨论】:

    标签: java runnable executorservice


    【解决方案1】:

    在访问共享状态(isShuttingDown)时,无论是读访问还是写访问,都需要始终持有相同的锁,才能在线程间保持一致的共享状态。 destroy() 方法不同步,因此在更改共享状态变量时不持有相同的对象锁(当前/“this”类实例上的隐式锁)。 这很可能是所有线程都无法正确看到 isShuttingDown 的原因...

    【讨论】:

    • 谢谢!这可能是原因之一。我的第一个尝试是:同步整个destroy()方法,但它随后出现死锁(destroy()在持有锁的同时等待线程完成,run()将在完成之前调度调度轮询()等待那个锁)。我的第二次尝试:使用块同步,即代替 awaitTermination,使用 isTerminate 并阻止该单行,如果它尚未终止,则休眠。这一次,它将释放锁,然后休眠,以便当前线程可以完成其任务。
    【解决方案2】:

    试试

    private volatile boolean isShuttingDown = false;
    

    Thread Pooling

    【讨论】:

      【解决方案3】:

      如果执行器正​​在关闭,javadoc 对新调度的行为不是很清楚。据我了解,它应该拒绝新的时间表。

      无论如何,轮询线程看不到您的标志的值,因为它没有同步。你需要同步它的所有访问,或者让它变得不稳定。另一种方法是使用 AtomicBoolean,它将为您封装。

      【讨论】:

      • 我做到了。在我的代码中,我有 isShuttingDown 的 setter 和 getter,它们是同步的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-11
      • 2012-03-16
      • 1970-01-01
      • 2011-01-18
      • 1970-01-01
      • 2020-04-22
      相关资源
      最近更新 更多