将代码从使用定时器移植到scheduledexecutorservice

时间:2009-03-12 01:55:52

标签: java timer executorservice

我正在尝试将代码从使用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;
         }
    }

}

我应该只使用ScheduledExecutorService替换A类和B类中的Timer实例,并将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中抛出的运行时异常会杀死一个线程并且无法进一步调度。我想避免这种情况,以便即使我有运行时异常,线程应该继续执行而不是停止。

3 个答案:

答案 0 :(得分:5)

注意:执行此操作的方式会泄漏线程!

如果您的班级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上,我希望它也可以在其他操作系统上发生。

当您不打算在单个对象实例上使用生命周期方法时,静态缓存线程池通常是一种安全的解决方案,因为您只保留与实际运行一样多的线程并且对于您创建的每个实例都没有一个尚未进行垃圾回收的实例。

答案 1 :(得分:3)

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

答案 2 :(得分:1)

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

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

如果你真的必须用它来重复任务,每个日程安排都应该是这样的:

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