Prioritätsbasierte Auftragsplanung in Java

Prioritätsbasierte Jobplanung in Java

1. Einführung

In einer Umgebung mit mehreren Threads müssen Aufgaben manchmal nach benutzerdefinierten Kriterien und nicht nur nach dem Erstellungszeitpunkt geplant werden.

Mal sehen, wie wir dies in Java erreichen können - mit einemPriorityBlockingQueue.

2. Überblick

Angenommen, wir haben Jobs, die wir basierend auf ihrer Priorität ausführen möchten:

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
}

Zu Demonstrationszwecken drucken wir den Auftragsnamen und die Priorität in derrun()-Methode.

Wir haben auchsleep() hinzugefügt, um einen länger laufenden Job zu simulieren. Während der Ausführung des Jobs werden mehr Jobs in der Prioritätswarteschlange gesammelt.

Schließlich istJobPriority eine einfache Aufzählung:

public enum JobPriority {
    HIGH,
    MEDIUM,
    LOW
}

3. BenutzerdefinierteComparator

Wir müssen einen Komparator schreiben, der unsere benutzerdefinierten Kriterien definiert. und inJava 8, it’s trivial:

Comparator.comparing(Job::getJobPriority);

4. Priority Job Scheduler

Nachdem alle Einstellungen vorgenommen wurden, implementieren wir jetzt einen einfachen Jobplaner, der einen einzelnen Thread-Executor verwendet, um nach Jobs inPriorityBlockingQueue zu suchen und diese auszuführen:

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. Der nächste auszuführende Job wird mit der Methodetake() aus der Warteschlange ausgewählt, mit der der Kopf der Warteschlange abgerufen und entfernt wird.

Der Client-Code muss jetzt nur nochscheduleJob() aufrufen, wodurch der Job zur Warteschlange hinzugefügt wird. DiepriorityQueue.add() stellen den Job an der entsprechenden Position in eine Warteschlange im Vergleich zu vorhandenen Jobs in der Warteschlange, wobei dieJobExecutionComparator verwendet werden.

Beachten Sie, dass die tatsächlichen Jobs mit einem separatenExecutorService mit einem dedizierten Thread-Pool ausgeführt werden.

5. Demo

Zum Schluss noch eine kurze Demonstration des Schedulers:

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
}

Um zu demonstrieren, dass die Jobs in der Reihenfolge ihrer Priorität ausgeführt werden, haben wirPOOL_SIZE als 1 beibehalten, obwohlQUEUE_SIZE 10 ist. Wir stellen dem Scheduler Aufträge mit unterschiedlicher Priorität zur Verfügung.

Hier ist eine Beispielausgabe, die wir für einen der Läufe erhalten haben:

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

Die Ausgabe kann über die Läufe variieren. Es sollte jedoch niemals einen Fall geben, in dem ein Job mit niedrigerer Priorität ausgeführt wird, selbst wenn die Warteschlange einen Job mit höherer Priorität enthält.

6. Fazit

In diesem kurzen Tutorial haben wir gesehen, wiePriorityBlockingQueue verwendet werden können, um Jobs in einer benutzerdefinierten Prioritätsreihenfolge auszuführen.

Wie üblich können Quelldateienover on GitHub gefunden werden.