2

何か (ランナブル) を定期的に実行するクラスを作成したいと思いますが、必要に応じて呼び出すことができます。全体をカプセル化できれば、次のメソッドを公開したいと思います。

public class SomeService implements Runnable {


  public run() {
    // the code to run at every interval
  }

  public static void start() { }
  public static void wakeup() { }
  public static void shutdown() { }

}

なんとかここまでたどり着きました。しかし、これが正しいアプローチであるかどうかはわかりません。

public class SomeService implements Runnable {

  private static SomeService service;
  private static Thread thread;
  static {
    start();
  }

  private boolean running = true;

  private SomeService() {
  }

  public void run() {
    while (running) {
      try {
        // do what needs to be done
        // perhaps peeking at a blocking queue
        // or checking for records in a database
        // trying to be independent of the communication
        System.out.println("what needs to be done");
        // wait for 15 seconds or until notify
        synchronized (thread) {
          try {
            thread.wait(15000);
          } catch (InterruptedException e) {
            System.out.println("interrupted");
          }
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  private static void start() {
    System.out.println("start");
    service = new SomeService();
    thread = new Thread(service);
    thread.setDaemon(true);
    thread.start();
  }

  public static void wakeup() {
    synchronized (thread) {
      thread.notify();
    }
  }

  public static void shutdown() {
    synchronized (thread) {
      service.running = false;
      thread.interrupt();
      try {
        thread.join();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    System.out.println("shutdown");
  }

  public static void main(String[] args) throws IOException {

    SomeService.wakeup();
    System.in.read();
    SomeService.wakeup();
    System.in.read();
    SomeService.shutdown();

  }

}

変数を揮発性として宣言する必要があることを懸念しています。また、「何をする必要があるか」の部分をチェックインする必要があることも懸念していますthread.isInterrupted()。これは正しいアプローチのように思えますか? これをエグゼキューターに翻訳する必要がありますか?スケジュールされたエグゼキュータで実行を強制するにはどうすればよいですか?

編集

エグゼキュータを試してみたところ、このアプローチは合理的であると思われます。どう思いますか?

public class SomeExecutorService implements Runnable {

  private static final SomeExecutorService runner 
    = new SomeExecutorService();

  private static final ScheduledExecutorService executor 
    = Executors.newSingleThreadScheduledExecutor();

  // properties

  ScheduledFuture<?> scheduled = null;

  // constructors

  private SomeExecutorService() {
  }

  // methods

  public void schedule(int seconds) {
    scheduled = executor.schedule(runner, seconds, TimeUnit.SECONDS);
  }

  public void force() {
    if (scheduled.cancel(false)) {
      schedule(0);
    }
  }

  public void run() {
    try {
      _logger.trace("doing what is needed");
    } catch (Exception e) {
      _logger.error("unexpected exception", e);
    } finally {
      schedule(DELAY_SECONDS);
    }
  }

  // static methods

  public static void initialize() {
    runner.schedule(0);
  }

  public static void wakeup() {
    runner.force();
  }

  public static void destroy() {
    executor.shutdownNow();
  }

}
4

3 に答える 3

4

まず第一に、おそらく自分で Runnable を実装したくないでしょう。Runnable を取り入れる必要があります。クラスが実行のために他のクラスに渡されることが予想される場合にのみ、Runnable を実装する必要があります。

なぜ単に ScheduledExecutorService をラップしないのでしょうか? これは簡単な(非常に貧弱ですが、機能するはずです)実装です。

public class PokeableService {

  private ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
  private final Runnable codeToRun;

  public PokeableService (Runnable toRun, long delay, long interval, TimeUnit units) {
    codeToRun = toRun;
    service.scheduleAtFixedRate(toRun, delay, interval, units);
  }

  public void poke () {
    service.execute(codeToRun);
  }
}
于 2012-06-14T16:49:17.613 に答える
1

変数は、同期ブロックで読み取られて変更されるため、揮発性である必要はありません。

Thread クラスは独自の同期を行うため、ロックとスレッドには別のオブジェクトを使用する必要があります。

シングルスレッドの ScheduledExecutorService を使用し、スリープを削除することをお勧めします。次に、現在のスリープ期間中にタスクを実行したい場合は、1 回の実行のためにもう一度エグゼキュータに送信できます。ScheduledExecutorService が拡張する ExecutorService の execute または submit メソッドを使用するだけです。

isInterrupted のチェックについては、do work 部分に多くの時間がかかる可能性があり、途中でキャンセルされる可能性があり、ブロックするメソッドを呼び出しておらず、何らかの方法で中断された例外をスローする場合に、これを行う必要があります。

于 2012-06-14T16:45:55.353 に答える
0

待機/通知を使用すると、より効率的な方法になります。また、「volatile」を使用する必要はなく、代替オブジェクトで同期することが競合を避けるために賢明であるという提案にも同意します。

その他のいくつかの提案:

  • 他の場所でスレッドを開始します。静的ブロックから開始することはお勧めできません
  • 実行ロジックを「execute()」メソッドなどに入れることが望ましいでしょう

このコードは、上記の提案を実装しています。また、SomeService 実行ロジックを実行するスレッドは 1 つだけであり、最後に完了してから INTERVAL ミリ秒後に発生することにも注意してください。手動でトリガーされた wakeUp() 呼び出しの後、重複して実行されることはありません。

public class SomeService implements Runnable {

  private static final INTERVAL = 15 * 1000;
  private Object svcSynchronizer = new Object();
  private boolean running = true;

  private SomeService() {
  }

  public void run() {
    while (running) {
      try {
        // do what needs to be done
        // perhaps peeking at a blocking queue
        // or checking for records in a database
        // trying to be independent of the communication
        System.out.println("what needs to be done");

        // wait for 15 seconds or until notify
        try {
          svcSynchronizer.wait(INTERVAL);
        } catch (InterruptedException e) {
          // ignore interruptions
        }

      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }


  public void wakeUp() {
    svcSynchronizer.notifyAll();
  }

  public void shutdown() {
    running = false;
    svcSynchronizer.notifyAll();
  }

}
于 2012-06-14T16:58:39.310 に答える