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カスタム

コンパレータ


カスタム基準を定義するコンパレータを書く必要があります。そして、リンクしている:/java-8-コンパレータ – 比較[Java 8、それは些細だ]

Comparator.comparing(Job::getJobPriority);


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

すべての設定が完了したら、今度は単純なジョブスケジューラを実装しましょう。これはシングルスレッドエグゼキュータを使用して

PriorityBlockingQueue

でジョブを検索し、それらを実行します。

public class PriorityJobScheduler {

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

    public PriorityJobScheduler(Integer poolSize, Integer queueSize) {
        priorityJobPoolExecutor = Executors.newFixedThreadPool(poolSize);
        priorityQueue = new PriorityBlockingQueue<Job>(
          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);
    }
}

  • ここで重要なのは、カスタムコンパレータを使用して

    Job

    タイプの

    PriorityBlockingQueue

    のインスタンスを作成することです** キューの先頭を取得および削除する

    take()

    メソッドを使用して、次に実行するジョブをキューから選択します。

クライアントコードは

scheduleJob()

を呼び出すだけでいい – ジョブをキューに追加します。

priorityQueue.add()

は、

JobExecutionComparator

を使用して、キュー内の既存のジョブと比較して適切な位置にあるジョブをキューに入れます。

実際のジョブは、専用のスレッドプールを持つ個別の

ExecutorService

を使用して実行されます。


5デモ

最後に、スケジューラについて簡単に説明します。

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

を使用してカスタムの優先順位でジョブを実行する方法を説明しました。

いつものように、ソースファイルはhttps://github.com/eugenp/tutorials/tree/master/core-java-concurrency[over on GitHub]にあります。