【问题标题】:Porting code from using timers to scheduledexecutorservice将代码从使用计时器移植到调度的执行器服务
【发布时间】:2009-03-12 01:55:51
【问题描述】:

我正在尝试将代码从使用 java timers 移植到使用 scheduledexecutorservice

我有以下用例

class A {

    public boolean execute() {
         try {
              Timer t = new Timer();
              t.schedule (new ATimerTask(), period, delay);
         } catch (Exception e) {
              return false;
         }
    }

}


class B {

    public boolean execute() {
         try {
              Timer t = new Timer();
              t.schedule (new BTimerTask(), period, delay);
         } catch (Exception e) {
              return false;
         }
    }

}

我是否应该将 A 类和 B 类中的 Timer 实例替换为 ScheduledExecutorService 并将 ATimerTask 和 BTimerTask 类设为 Runnable 类,例如

class B {

    public boolean execute() {
         try {
              final ScheduledExecutorService scheduler = 
   Executors.newScheduledThreadPool(1);

              scheduler.scheduleWithFixedDelay (new BRunnnableTask(), period, delay);
         } catch (Exception e) {
              return false;
         }
    }

}

这对吗?

编辑:移植的主要动机之一是因为 TimerTask 中抛出的运行时异常会杀死一个线程并且无法进一步安排它。我想避免这种情况,这样即使我有运行时异常,线程也应该继续执行而不是停止。

【问题讨论】:

    标签: java timer executorservice


    【解决方案1】:

    注意:您这样做的方式会泄漏线程!

    如果您的班级 B 将被保留并且每个实例最终将被关闭或关闭或释放,我会这样做:

    class B {
      final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    
      public boolean execute() {
        try {
          scheduler.scheduleWithFixedDelay(new BRunnnableTask(), period, delay);
          return true;
        } catch (Exception e) {
          return false;
        }
      }
    
      public void close() {
        scheduler.shutdownNow();
      }
    }
    

    如果您不对每个实例进行这种清理,那么我会改为这样做:

    class B {
      static final ScheduledExecutorService SCHEDULER = Executors.newCachedThreadPool();
    
      public boolean execute() {
        try {
          SCHEDULER.scheduleWithFixedDelay(new BRunnnableTask(), period, delay);
          return true;
        } catch (Exception e) {
          return false;
        }
      }
    }
    

    您在代码中分配的每个ExecutorService 分配一个Thread。如果您创建了许多类B 的实例,那么每个实例将被分配一个Thread。如果这些不能快速收集垃圾,那么您最终可能会分配数千个线程(但未使用,只是分配),并且您可能会崩溃整个服务器,使机器上的每个进程都饿死,而不仅仅是您自己的 JVM。我已经看到它发生在 Windows 上,我希望它也可以发生在其他操作系统上。

    当您不打算在单个对象实例上使用生命周期方法时,静态缓存线程池通常是一种安全的解决方案,因为您只会保留实际运行的线程数运行而不是为您创建的每个尚未被垃圾收集的实例创建一个。

    【讨论】:

      【解决方案2】:

      看起来不错。根据您正在做的事情,您可能希望保留 executor service 作为成员,以便您可以再次使用它。此外,您可以从 scheduleXX() 方法中获取 ScheduledFuture。这很有用,因为您可以对其调用 get() 以将定时线程中发生的任何异常拉回控制线程进行处理。

      【讨论】:

      • 这很重要! Executors 在用于重复任务时通常很狡猾,因为它们会吞下异常——实际上并非如此,异常存储在 FutureTask 中,但由于在执行重复任务时没有人调用 get(),所以效果是一样的。跨度>
      【解决方案3】:

      目前在 Executors 框架中没有很好的方法来处理重复的任务。

      它的设计确实没有考虑到这个用例,也没有现实的方法来避免吞下异常。

      如果您确实必须将其用于重复任务,则每个调度应如下所示:

      scheduler.scheduleWithFixedDelay(new Runnable() {
        public void run() {
           try {
             .. your normal code here...
           } catch (Throwable t) {
             // handle exceptions there
           }
        }
      }, period, delay);
      

      【讨论】:

        猜你喜欢
        • 2011-05-10
        • 1970-01-01
        • 2010-12-13
        • 1970-01-01
        • 2019-08-26
        • 1970-01-01
        • 1970-01-01
        • 2021-05-01
        • 1970-01-01
        相关资源
        最近更新 更多