HOWTO · Java

Java のスケジューリング

この記事では、Java でタスクをスケジュールする方法を紹介します。

スケジューリングは、一定の時間間隔でタスクまたは機能を実行するプロセスです。スケジューリングは、バッチプロセスの実行、誕生日の願いなどのイベントベースのトリガーの送信、プッシュ通知の送信での使用法を見つけます。これらのプロセスは、指定された条件と時間間隔に基づいて、明確に定義された時間に実行する必要があります。

Java の ScheduledExecutorService インターフェイスメソッドを使用してプロセスをスケジュールする

package scheduling;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class ScheduleTask {
  private static int counter = 0;

  public static void main(String[] args) throws InterruptedException {
    ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
    Runnable executeTask = () -> {
      counter++;
      System.out.println("Task" + counter);
    };

    ScheduledFuture<?> scheduleAtFixedRate =
        service.scheduleAtFixedRate(executeTask, 5, 1, TimeUnit.SECONDS);

    while (true) {
      Thread.sleep(1000);
      if (counter == 5) {
        System.out.println("Stopping the scheduled task!");
        scheduleAtFixedRate.cancel(true);
        service.shutdown();
        break;
      }
    }
  }
}

上記のコードでは、最初に静的カウンター変数がゼロ値に初期化されます。newScheduledThreadPool は、Executors クラスの静的メソッドです。このメソッドは、引数リストで指定されたパラメーターを使用して定期的に実行する必要があるスレッドプールを作成します。したがって、この場合、メソッドの引数リストのパラメーターがスレッドプールの数を定義するため、実行用に 1つのスレッドが作成されます。このメソッドは、service 参照変数に格納される ScheduledExecutorService のインスタンスを返し、プールサイズがゼロ未満の場合は IllegalArgumentException をスローします。

Runnable は、任意のクラスが拡張できるインターフェイスであり、機能インターフェイスです。run メソッドとして静的メソッドが 1つだけあり、スレッドが実行することを目的としています。記号 ()-> 括弧と矢印で表されるラムダ式を使用して、構造体は、run メソッドに引数を渡していないことを示します。run メソッドの定義でステートメントを定義しました。ブロック内に存在するステートメントは、カウンター変数をインクリメントし、コンソール出力にタスクを出力します。これらのすべてのステートメントは、executeTask 変数と呼ばれる変数によって参照されます。

service 参照変数は、ScheduledExecutorService インターフェースの scheduleAtFixedRate メソッドを呼び出します。このメソッドは、最初の遅延の後、指定された期間内に実行する必要がある定期的なタスクを作成して実行します。これは 4つのパラメーターを取ります。実行することになっている runnable コマンド、initialDelay 変数は最初の実行を遅らせる時間、period は連続する実行間の期間、unit は秒単位の時間の単位です、分、および時間。メソッドが呼び出されるとすぐに、スレッドの実行が開始されます。

while ループでは、最初に、現在実行中のスレッドが強制的にスリープ状態になります。Thread.sleep() メソッドは、現在実行中のスレッドを定義された時間一時的に停止します。パラメータは、現在の作業スレッドが停止するミリ秒単位の数値です。このメソッドは、値が負の場合は IllegalArgumentException をスローし、現在のスレッドが中断された場合は InterruptedException をスローします。後で、カウンターの値が定義された値でチェックされます。このチェックは、while メソッドを一定の回数実行するために適用されます。それ以外の場合、ブロックは無限の数で動作し、終了することはありません。while ブロック内で、scheduleAtFixedRate 変数は cancel メソッドを呼び出します。これにより、スレッドの現在の実行がキャンセルされます。この関数は、現在実行中のスレッドが割り込むことができるかどうかを示すブールパラメータも取ります。

メソッド service.shutdown は、以前に送信されたタスクが実行されることになっているシャットダウンプロセスを開始し、新しいものは何も受け入れられません。

上記のコードブロックの出力は次のとおりです。

Task1
Task2
Task3
Task4
Task5
Stopping the scheduled task!

Java の Timer クラスを使用してスレッドをスケジュールする

以下は、コンストラクターを使用して 2つのユーザー定義クラスオブジェクトをインスタンス化する単純なコードブロックです。TimerImplementation は、同じもののユーザー定義クラスです。新しいスレッドを作成する Timer のインスタンスが作成されます。新しく作成されたタイマーのオブジェクトは、scheduleAtFixedRate メソッドを呼び出します。このメソッドは、パラメータをスケジュールする必要のある task として受け取り、delay はタスクをミリ秒単位で遅延させ、perod は連続実行のミリ秒単位の時間です。

package timer;

import java.util.Timer;

public class UsingTimerClass {
  public static void main(String[] args) {
    TimerImplementation timer1 = new TimerImplementation("Thread1");
    TimerImplementation timer2 = new TimerImplementation("Thread2");

    Timer t = new Timer();
    t.scheduleAtFixedRate(timer1, 0, 2000);
    t.scheduleAtFixedRate(timer2, 0, 1000);
  }
}

以下は、ユーザー定義クラスの実装です。TimerImplementation クラスは、run という名前の単一の抽象メソッドを保持する TimerTask 抽象クラスを拡張します。TimerTask クラスをユーザー定義クラスで拡張すると、run メソッドがオーバーライドされます。

このクラスには、ユーザー定義名をスレッドオブジェクトに設定するユーザー定義コンストラクターが 1つあります。

スレッド run メソッドで実際のロジックを与えることができます。これには、現在実行中のスレッドの名前を出力する print としてのステートメントが含まれています。Thread.currentThread().getName() は、現在実行中のスレッドの名前を返します。sleep メソッドは、実行を 1000 ミリ秒中断する Thread1 を介して呼び出されます。sleep メソッドは、スレッドが現在のスレッドに割り込むと InterruptedException をスローします。そのため、try-catch ブロックで囲まれます。

package timer;

import java.util.TimerTask;

public class TimerImplementation extends TimerTask {
  private String name;

  public TimerImplementation(String n) {
    this.name = n;
  }

  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName() + " " + name);
    if ("Thread1".equalsIgnoreCase(name)) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

以下は、上記のコードの終わりのない出力です。run メソッドで終了条件を定義していないため、メインスレッドの実行を強制終了するために外部停止が適用されるまで、スレッドは無限に実行されます。

Timer-0 Thread1
Timer-0 Thread2
Timer-0 Thread2
Timer-0 Thread2
Timer-0 Thread1
Timer-0 Thread2
Timer-0 Thread2
Timer-0 Thread1
Timer-0 Thread2