2

我正在尝试将代码从使用 java计时器移植到使用scheduleexecutorservice

我有以下用例

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 中引发的运行时异常会杀死一个线程并且无法进一步安排它。我想避免这种情况,这样即使我有运行时异常,线程也应该继续执行而不是停止。

4

3 回答 3

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

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

于 2009-03-12T05:29:56.943 回答
3

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

于 2009-03-12T02:12:04.643 回答
1

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

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

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

scheduler.scheduleWithFixedDelay(new Runnable() {
  public void run() {
     try {
       .. your normal code here...
     } catch (Throwable t) {
       // handle exceptions there
     }
  }
}, period, delay);
于 2010-02-25T14:04:27.450 回答