Prioritätsbasierte Auftragsplanung in Java

1. Einleitung

In einer Umgebung mit mehreren Threads müssen wir manchmal Aufgaben basierend auf benutzerdefinierten Kriterien planen, anstatt nur die Erstellungszeit.

Mal sehen, wie wir dies in Java erreichen können - mithilfe einer PriorityBlockingQueue .

2. Übersicht

Nehmen wir an, 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 der run () -Methode.

Wir haben auch sleep () 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 ist JobPriority eine einfache Aufzählung:

public enum JobPriority { HIGH, MEDIUM, LOW }

3. Benutzerdefinierter Komparator

Wir müssen einen Komparator schreiben, der unsere benutzerdefinierten Kriterien definiert. und in Java 8 ist es 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 in der PriorityBlockingQueue 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); } }

Der Schlüssel hier besteht darin, eine Instanz von PriorityBlockingQueue vom Jobtyp mit einem benutzerdefinierten Komparator zu erstellen . Der nächste auszuführende Job wird mit der Methode take () aus der Warteschlange ausgewählt, mit der der Kopf der Warteschlange abgerufen und entfernt wird.

Der Client-Code muss jetzt nur noch ScheduleJob () aufrufen, wodurch der Job zur Warteschlange hinzugefügt wird. Die priorityQueue.add () stellt den Job mithilfe des JobExecutionComparator an einer geeigneten Position im Vergleich zu vorhandenen Jobs in der Warteschlange in die Warteschlange .

Beachten Sie, dass die tatsächlichen Jobs mit einem separaten ExecutorService 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 wir POOL_SIZE als 1 beibehalten , obwohl QUEUE_SIZE 10 ist. Wir stellen dem Scheduler Jobs 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 je nach Lauf 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, wie PriorityBlockingQueue verwendet werden kann, um Jobs in einer benutzerdefinierten Prioritätsreihenfolge auszuführen.

Quelldateien finden Sie wie gewohnt auf GitHub.