Pianificazione dei lavori basata sulle priorità in Java

1. Introduzione

In un ambiente multi-thread, a volte è necessario pianificare le attività in base a criteri personalizzati anziché solo al tempo di creazione.

Vediamo come possiamo ottenere questo risultato in Java, utilizzando PriorityBlockingQueue .

2. Panoramica

Supponiamo di avere lavori che vogliamo eseguire in base alla loro priorità:

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 }

A scopo dimostrativo, stiamo stampando il nome e la priorità del lavoro nel metodo run () .

Abbiamo anche aggiunto sleep () in modo da simulare un lavoro di lunga durata; mentre il lavoro è in esecuzione, più lavori verranno accumulati nella coda di priorità.

Infine, JobPriority è una semplice enumerazione :

public enum JobPriority { HIGH, MEDIUM, LOW }

3. Comparatore personalizzato

Dobbiamo scrivere un comparatore che definisca i nostri criteri personalizzati; e, in Java 8, è banale:

Comparator.comparing(Job::getJobPriority);

4. Priority Job Scheduler

Con tutte le impostazioni eseguite, implementiamo ora un semplice pianificatore di lavori, che impiega un singolo esecutore di thread per cercare i lavori in PriorityBlockingQueue e li esegue:

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); } }

La chiave qui è creare un'istanza di PriorityBlockingQueue di tipo Job con un comparatore personalizzato. Il lavoro successivo da eseguire viene prelevato dalla coda utilizzando il metodo take () che recupera e rimuove la testa della coda.

Il codice client ora deve semplicemente chiamare scheduleJob () , che aggiunge il lavoro alla coda. Il priorityQueue.add () mette in coda il lavoro nella posizione appropriata rispetto ai posti di lavoro esistenti nella coda, utilizzando il JobExecutionComparator .

Si noti che i lavori effettivi vengono eseguiti utilizzando un ExecutorService separato con un pool di thread dedicato.

5. Demo

Infine, ecco una rapida dimostrazione dello scheduler:

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 }

Per dimostrare che i lavori vengono eseguiti in ordine di priorità, abbiamo mantenuto POOL_SIZE come 1 anche se QUEUE_SIZE è 10. Forniamo lavori con priorità variabile allo scheduler.

Ecco un output di esempio che abbiamo ottenuto per una delle prove:

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

L'output potrebbe variare a seconda delle esecuzioni. Tuttavia, non dovremmo mai avere un caso in cui viene eseguito un lavoro con priorità più bassa anche quando la coda contiene un lavoro con priorità più alta.

6. Conclusione

In questo breve tutorial, abbiamo visto come PriorityBlockingQueue può essere utilizzato per eseguire i lavori in un ordine di priorità personalizzato.

Come al solito, i file sorgente possono essere trovati su GitHub.