Event-driven programming is a paradigm where the flow of the program is determined by events—such as user actions, sensor inputs, or messages from other systems. In this model, queues are essential as buffers that temporarily store events before they are processed.
An event queue decouples the generation of events from their processing. Events are enqueued as they occur and later dequeued for handling, usually in a First-In-First-Out (FIFO) manner. This separation allows for flexible, asynchronous event handling and helps prevent missed or lost events in high-throughput systems.
Below is a runnable example simulating an event-driven system using a Queue
to process application events.
import java.util.*;
class Event {
String type;
String payload;
Event(String type, String payload) {
this.type = type;
this.payload = payload;
}
@Override
public String toString() {
return "Event{" + "type='" + type + '\'' + ", payload='" + payload + '\'' + '}';
}
}
public class EventDrivenExample {
public static void main(String[] args) {
Queue<Event> eventQueue = new LinkedList<>();
// Simulate event producers
eventQueue.offer(new Event("CLICK", "Button A"));
eventQueue.offer(new Event("HOVER", "Image X"));
eventQueue.offer(new Event("INPUT", "User typed 'Hello'"));
// Event loop - simulate event consumer
while (!eventQueue.isEmpty()) {
Event event = eventQueue.poll(); // Fetch next event
handleEvent(event);
}
}
static void handleEvent(Event event) {
// Event dispatch based on type
switch (event.type) {
case "CLICK" -> System.out.println("Handling click on: " + event.payload);
case "HOVER" -> System.out.println("Handling hover over: " + event.payload);
case "INPUT" -> System.out.println("Processing input: " + event.payload);
default -> System.out.println("Unknown event type: " + event);
}
}
}
Handling click on: Button A
Handling hover over: Image X
Processing input: User typed 'Hello'
In more advanced systems, event queues might be backed by thread-safe queues (e.g., BlockingQueue
) or integrated with reactive or asynchronous frameworks.
Using queues for event handling provides a scalable and modular structure for asynchronous programming. By decoupling event producers from consumers, the system becomes more responsive, manageable, and ready for concurrency.
In many event-driven or task-processing systems, not all tasks are equal—some must be handled before others based on priority. Priority scheduling ensures that higher-priority tasks are processed first, improving responsiveness for critical events.
Java’s PriorityQueue
is an ideal data structure for implementing such scheduling. Unlike a regular queue that processes elements in FIFO order, a PriorityQueue
retrieves elements according to their priority (natural order or a custom comparator).
Internally, PriorityQueue
is usually implemented as a binary heap, providing efficient insertion and removal operations with average time complexity of O(log n).
Comparable
, the queue uses their compareTo()
method to decide priority.Comparator
to define a custom ordering, useful for complex priority logic.Here, tasks with integer priority values are scheduled, where smaller numbers indicate higher priority.
import java.util.PriorityQueue;
class Task implements Comparable<Task> {
String name;
int priority; // lower value = higher priority
Task(String name, int priority) {
this.name = name;
this.priority = priority;
}
@Override
public int compareTo(Task other) {
return Integer.compare(this.priority, other.priority);
}
@Override
public String toString() {
return name + " (priority " + priority + ")";
}
}
public class PrioritySchedulingExample {
public static void main(String[] args) {
PriorityQueue<Task> taskQueue = new PriorityQueue<>();
taskQueue.offer(new Task("Low priority task", 5));
taskQueue.offer(new Task("High priority task", 1));
taskQueue.offer(new Task("Medium priority task", 3));
System.out.println("Processing tasks in priority order:");
while (!taskQueue.isEmpty()) {
System.out.println("Executing: " + taskQueue.poll());
}
}
}
Output:
Processing tasks in priority order:
Executing: High priority task (priority 1)
Executing: Medium priority task (priority 3)
Executing: Low priority task (priority 5)
Suppose tasks have string priorities like "HIGH"
, "MEDIUM"
, "LOW"
; you can define a custom comparator to assign ordering.
import java.util.*;
class StringPriorityTask {
String name;
String priority; // "HIGH", "MEDIUM", "LOW"
StringPriorityTask(String name, String priority) {
this.name = name;
this.priority = priority;
}
@Override
public String toString() {
return name + " (" + priority + ")";
}
}
public class CustomComparatorExample {
public static void main(String[] args) {
// Define priority order
Map<String, Integer> priorityMap = Map.of("HIGH", 1, "MEDIUM", 2, "LOW", 3);
Comparator<StringPriorityTask> comparator = Comparator.comparingInt(
task -> priorityMap.getOrDefault(task.priority, Integer.MAX_VALUE));
PriorityQueue<StringPriorityTask> queue = new PriorityQueue<>(comparator);
queue.offer(new StringPriorityTask("Task A", "LOW"));
queue.offer(new StringPriorityTask("Task B", "HIGH"));
queue.offer(new StringPriorityTask("Task C", "MEDIUM"));
System.out.println("Processing tasks with custom priority:");
while (!queue.isEmpty()) {
System.out.println("Executing: " + queue.poll());
}
}
}
Output:
Processing tasks with custom priority:
Executing: Task B (HIGH)
Executing: Task C (MEDIUM)
Executing: Task A (LOW)
PriorityQueue
empowers event-driven or task-based applications to efficiently handle work based on priority rather than arrival order. By leveraging natural ordering or custom comparators, you can tailor scheduling logic to fit diverse scenarios—from urgent system alerts to background maintenance jobs.
Use PriorityQueue
whenever task prioritization is critical for system responsiveness and correctness.