Портирование кода с использованием таймеров на scheduledexecutorservice - PullRequest
2 голосов
/ 12 марта 2009

Я пытаюсь перенести код с использования 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;
         }
    }

}

Должен ли я просто заменить экземпляры Timer в классе A и классе B на 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, убивают этот поток и его нельзя запланировать дальше. Я хочу избежать этого случая, так что даже если у меня есть исключение времени выполнения, поток должен продолжать выполняться, а не останавливаться.

Ответы [ 3 ]

5 голосов
/ 12 марта 2009

ПРИМЕЧАНИЕ. То, как вы это сделали, приведет к утечке потоков!

Если ваш класс 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, и я ожидаю, что это может произойти и в других ОС.

Статический пул потоков в кэше очень часто является безопасным решением, когда вы не намерены использовать методы жизненного цикла в отдельных экземплярах объекта, поскольку вы сохраняете столько потоков, сколько фактически выполняется и не один для каждого создаваемого вами экземпляра, который еще не собран мусором.

3 голосов
/ 12 марта 2009

выглядит нормально. В зависимости от того, что вы делаете, вы можете захотеть сохранить службу executor в качестве члена, чтобы вы могли использовать ее снова. Кроме того, вы можете получить ScheduledFuture обратно из методов scheduleXX (). Это полезно, потому что вы можете вызвать get () для него, чтобы извлечь любые исключения, которые происходят в синхронизированном потоке, обратно в ваш управляющий поток для обработки.

1 голос
/ 25 февраля 2010

В настоящее время нет хорошего способа обработки повторяющихся задач в среде Executors.

Он действительно не был разработан с учетом этого варианта использования, и не существует реалистичного способа избежать проглатывания исключений.

Если вы действительно должны использовать его для повторения задач, каждое расписание должно выглядеть примерно так:

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