Javaにおける優先順位ベースのジョブスケジューリング

Javaでの優先度ベースのジョブスケジューリング

1. 前書き

マルチスレッド環境では、作成時間だけではなく、カスタム条件に基づいてタスクをスケジュールする必要がある場合があります。

PriorityBlockingQueueを使用して、Javaでこれを実現する方法を見てみましょう。

2. 概要

優先度に基づいて実行したいジョブがあるとしましょう:

public class Job implements Runnable {
    private String jobName;
    private JobPriority jobPriority;

    @Override
    public void run() {
        System.out.println("Job:" + jobName +
          " Priority:" + jobPriority);
        Thread.sleep(1000); // to simulate actual execution time
    }

    // standard setters and getters
}

デモンストレーションの目的で、run()メソッドでジョブ名と優先度を出力しています。

また、より長時間実行されるジョブをシミュレートするために、sleep()を追加しました。ジョブの実行中は、より多くのジョブが優先キューに蓄積されます。

最後に、JobPriorityは単純な列挙型です。

public enum JobPriority {
    HIGH,
    MEDIUM,
    LOW
}

3. カスタムComparator

カスタム基準を定義するコンパレータを作成する必要があります。そして、Java 8, it’s trivialで:

Comparator.comparing(Job::getJobPriority);

4. 優先ジョブスケジューラ

すべてのセットアップが完了したら、単純なジョブスケジューラを実装しましょう。これは、シングルスレッドエグゼキュータを使用してPriorityBlockingQueue内のジョブを検索し、それらを実行します。

public class PriorityJobScheduler {

    private ExecutorService priorityJobPoolExecutor;
    private ExecutorService priorityJobScheduler
      = Executors.newSingleThreadExecutor();
    private PriorityBlockingQueue priorityQueue;

    public PriorityJobScheduler(Integer poolSize, Integer queueSize) {
        priorityJobPoolExecutor = Executors.newFixedThreadPool(poolSize);
        priorityQueue = new PriorityBlockingQueue(
          queueSize,
          Comparator.comparing(Job::getJobPriority));
        priorityJobScheduler.execute(() -> {
            while (true) {
                try {
                    priorityJobPoolExecutor.execute(priorityQueue.take());
                } catch (InterruptedException e) {
                    // exception needs special handling
                    break;
                }
            }
        });
    }

    public void scheduleJob(Job job) {
        priorityQueue.add(job);
    }
}

The key here is to create an instance of PriorityBlockingQueue of Job type with a custom comparator.次に実行するジョブは、キューの先頭を取得して削除するtake()メソッドを使用してキューから選択されます。

クライアントコードは、scheduleJob()を呼び出すだけで済みます。これにより、ジョブがキューに追加されます。 priorityQueue.add()は、JobExecutionComparatorを使用して、キュー内の既存のジョブと比較して、適切な位置にジョブをキューに入れます。

実際のジョブは、専用のスレッドプールを持つ個別のExecutorServiceを使用して実行されることに注意してください。

5. Demo

最後に、スケジューラの簡単なデモンストレーションを次に示します。

private static int POOL_SIZE = 1;
private static int QUEUE_SIZE = 10;

@Test
public void whenMultiplePriorityJobsQueued_thenHighestPriorityJobIsPicked() {
    Job job1 = new Job("Job1", JobPriority.LOW);
    Job job2 = new Job("Job2", JobPriority.MEDIUM);
    Job job3 = new Job("Job3", JobPriority.HIGH);
    Job job4 = new Job("Job4", JobPriority.MEDIUM);
    Job job5 = new Job("Job5", JobPriority.LOW);
    Job job6 = new Job("Job6", JobPriority.HIGH);

    PriorityJobScheduler pjs = new PriorityJobScheduler(
      POOL_SIZE, QUEUE_SIZE);

    pjs.scheduleJob(job1);
    pjs.scheduleJob(job2);
    pjs.scheduleJob(job3);
    pjs.scheduleJob(job4);
    pjs.scheduleJob(job5);
    pjs.scheduleJob(job6);

    // clean up
}

ジョブが優先順位に従って実行されることをデモするために、QUEUE_SIZEが10であっても、POOL_SIZEを1のままにしました。 スケジューラにさまざまな優先度のジョブを提供します。

実行の1つについて得たサンプル出力を次に示します。

Job:Job3 Priority:HIGH
Job:Job6 Priority:HIGH
Job:Job4 Priority:MEDIUM
Job:Job2 Priority:MEDIUM
Job:Job1 Priority:LOW
Job:Job5 Priority:LOW

出力は実行によって異なる場合があります。 ただし、キューに優先度の高いジョブが含まれている場合でも、優先度の低いジョブが実行されることはありません。

6. 結論

このクイックチュートリアルでは、PriorityBlockingQueueを使用してカスタムの優先順位でジョブを実行する方法を説明しました。

いつものように、ソースファイルはover on GitHubで見つけることができます。