-
-
Notifications
You must be signed in to change notification settings - Fork 26.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Scheduler Pattern] (Add) scheduler pattern
- Loading branch information
Showing
16 changed files
with
543 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
--- | ||
title: Scheduler Pattern | ||
category: Creational | ||
language: en | ||
tag: | ||
--- | ||
|
||
## Name | ||
Scheduler Design Pattern | ||
|
||
## Intent | ||
The Scheduler Design Pattern is used to manage and coordinate the execution of tasks or jobs in a system. It provides a mechanism for scheduling and executing tasks at specific times, intervals, or in response to certain events. This pattern is especially useful when dealing with asynchronous operations, background processing, and resource allocation. | ||
|
||
## Explanation | ||
The Scheduler Design Pattern is designed to decouple task scheduling from the actual execution of tasks. It abstracts the scheduling logic, making it possible to change or extend the scheduling behavior without affecting the tasks themselves. This pattern allows for efficient resource utilization, load balancing, and prioritization of tasks. | ||
|
||
## Class diagram | ||
![Scheduler Pattern](etc/scheduler.png) | ||
|
||
## Applicability | ||
The Scheduler Design Pattern is applicable in various scenarios, including but not limited to: | ||
|
||
- **Task Queue Management**: When you need to manage a queue of tasks to be executed, ensuring tasks are executed in a specific order, on specific resources, or with certain priorities. | ||
|
||
- **Background Processing**: In applications requiring background jobs, such as processing user requests asynchronously, sending emails, or performing periodic maintenance tasks. | ||
|
||
- **Resource Allocation**: For managing shared resources, like database connections or thread pools, to ensure fair allocation among competing tasks. | ||
|
||
- **Real-time Systems**: In systems where tasks need to be executed at precise times or in response to specific events, such as in real-time simulations or monitoring systems. | ||
|
||
## Known uses | ||
The Scheduler Design Pattern is used in various software applications and frameworks, including: | ||
|
||
- Operating systems for managing processes. | ||
- Java: The Java `ScheduledExecutorService` class is an implementation of the Scheduler Design Pattern, allowing the scheduling of tasks at fixed rate or with fixed delay. | ||
|
||
## Consequences | ||
The Scheduler Design Pattern offers several advantages: | ||
- **Flexibility**: It allows for dynamic scheduling of tasks, making it adaptable to changing requirements. | ||
- **Efficiency**: Tasks can be optimized for resource utilization, and parallel execution can be managed effectively. | ||
- **Maintainability**: Separating scheduling logic from task execution simplifies maintenance and debugging. | ||
|
||
However, it also has some potential drawbacks: | ||
- **Complexity**: Implementing a scheduler can be complex, especially in systems with intricate scheduling requirements. | ||
- **Overhead**: Maintaining a scheduler adds some overhead to the system. | ||
|
||
|
||
## Related patterns | ||
The Scheduler Design Pattern is related to other design patterns, including: | ||
- **Observer Pattern**: When tasks need to be scheduled in response to specific events or changes in the system, the Observer Pattern can be used in conjunction with the Scheduler Pattern. | ||
- **Command Pattern**: Tasks to be executed by the scheduler can often be encapsulated using the Command Pattern, allowing for easy parameterization and queuing. | ||
|
||
## Credits | ||
- [Wikipedia: Scheduling (computing)](https://en.wikipedia.org/wiki/Scheduling_(computing)) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
@startuml | ||
|
||
class Task { | ||
-id: int | ||
-totalExecutionTime: int | ||
-priority: int | ||
-- | ||
+Task(id: int, totalExecutionTime: int, priority: int) | ||
+Task(id: int, totalExecutionTime: int) | ||
+getId(): int | ||
+getTotalExecutionTime(): int | ||
+getPriority(): int | ||
} | ||
|
||
interface TaskScheduler { | ||
+scheduleTask(task: Task): void | ||
+update(int deltaTime): void | ||
} | ||
|
||
class FirstComeFirstServedScheduler extends TaskScheduler {} | ||
class PriorityScheduler extends TaskScheduler {} | ||
class RoundRobinScheduler extends TaskScheduler {} | ||
class ShortestRemainingTimeFirstScheduler extends TaskScheduler {} | ||
|
||
class Simulator { | ||
-scheduler: TaskScheduler | ||
-Map<Integer, List<Task>> tasks | ||
-deltaTime: int | ||
-simulateTime: int | ||
-LinkedHashMap<Integer, Integer> taskCompletedOrder | ||
-elapsedTime: int | ||
-- | ||
+Simulator(scheduler: TaskScheduler, tasks: Map<Integer, List<Task>>, deltaTime: int, simulateTime: int) | ||
+simulate(): LinkedHashMap<Integer, Integer> | ||
} | ||
|
||
Task -- TaskScheduler : "1..*" | ||
TaskScheduler -- Simulator : "1" | ||
Simulator ..> Task : "1..*" | ||
|
||
@enduml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>com.iluwatar</groupId> | ||
<artifactId>java-design-patterns</artifactId> | ||
<version>1.26.0-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>scheduler</artifactId> | ||
|
||
<properties> | ||
<maven.compiler.source>17</maven.compiler.source> | ||
<maven.compiler.target>17</maven.compiler.target> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
</properties> | ||
<dependencies> | ||
<dependency> | ||
<groupId>org.junit.jupiter</groupId> | ||
<artifactId>junit-jupiter-api</artifactId> | ||
<version>5.9.2</version> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
35 changes: 35 additions & 0 deletions
35
scheduler/src/main/java/com/iluwatar/scheduler/FirstComeFirstServedScheduler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.iluwatar.scheduler; | ||
|
||
import java.beans.PropertyChangeEvent; | ||
import java.beans.PropertyChangeListener; | ||
import java.util.LinkedList; | ||
import java.util.Queue; | ||
|
||
public class FirstComeFirstServedScheduler implements TaskScheduler, PropertyChangeListener { | ||
private final Queue<Task> taskQueue = new LinkedList<>(); | ||
|
||
@Override | ||
public void scheduleTask(Task task) { | ||
task.getSupport().addPropertyChangeListener(this); | ||
taskQueue.add(task); | ||
} | ||
|
||
@Override | ||
public void update(int deltaTime) { | ||
Task task = taskQueue.peek(); | ||
if (task == null) return; | ||
task.execute(deltaTime); | ||
} | ||
|
||
@Override | ||
public void propertyChange(PropertyChangeEvent evt) { | ||
if (Task.COMPLETE_PROPERTY.equals(evt.getPropertyName())) { | ||
onTaskComplete(evt); | ||
} | ||
} | ||
|
||
private void onTaskComplete(PropertyChangeEvent evt) { | ||
Task task = (Task) evt.getSource(); | ||
taskQueue.remove(task); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
scheduler/src/main/java/com/iluwatar/scheduler/PriorityScheduler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.iluwatar.scheduler; | ||
|
||
import java.beans.PropertyChangeEvent; | ||
import java.beans.PropertyChangeListener; | ||
import java.util.PriorityQueue; | ||
import java.util.Queue; | ||
|
||
public class PriorityScheduler implements TaskScheduler, PropertyChangeListener { | ||
private final Queue<Task> taskQueue = | ||
new PriorityQueue<>( | ||
(task1, task2) -> { | ||
if (task2.getPriority() != task1.getPriority()) | ||
return task2.getPriority() - task1.getPriority(); | ||
return task1.getId() - task2.getId(); // lower id (earlier task) has higher priority | ||
}); | ||
|
||
@Override | ||
public void scheduleTask(Task task) { | ||
task.getSupport().addPropertyChangeListener(this); | ||
taskQueue.add(task); | ||
} | ||
|
||
@Override | ||
public void update(int deltaTime) { | ||
Task task = taskQueue.peek(); | ||
if (task == null) return; | ||
task.execute(deltaTime); | ||
} | ||
|
||
@Override | ||
public void propertyChange(PropertyChangeEvent evt) { | ||
if (Task.COMPLETE_PROPERTY.equals(evt.getPropertyName())) { | ||
onTaskComplete(evt); | ||
} | ||
} | ||
|
||
private void onTaskComplete(PropertyChangeEvent evt) { | ||
Task task = (Task) evt.getSource(); | ||
taskQueue.remove(task); | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
scheduler/src/main/java/com/iluwatar/scheduler/RoundRobinScheduler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.iluwatar.scheduler; | ||
|
||
import java.util.LinkedList; | ||
import java.util.Queue; | ||
|
||
public class RoundRobinScheduler implements TaskScheduler { | ||
private final Queue<Task> taskQueue = new LinkedList<>(); | ||
|
||
@Override | ||
public void scheduleTask(Task task) { | ||
taskQueue.add(task); | ||
} | ||
|
||
@Override | ||
public void update(int deltaTime) { | ||
Task task = taskQueue.poll(); | ||
if (task == null) return; | ||
task.execute(deltaTime); | ||
if (!task.isComplete()) taskQueue.add(task); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
scheduler/src/main/java/com/iluwatar/scheduler/ShortestRemainingTimeFirstScheduler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.iluwatar.scheduler; | ||
|
||
import java.util.Comparator; | ||
import java.util.PriorityQueue; | ||
import java.util.Queue; | ||
|
||
public class ShortestRemainingTimeFirstScheduler implements TaskScheduler { | ||
private final Queue<Task> taskQueue = | ||
new PriorityQueue<>( | ||
(task1, task2) -> { | ||
if (task2.getRemainingTime() != task1.getRemainingTime()) | ||
return task1.getRemainingTime() - task2.getRemainingTime(); | ||
return task1.getId() - task2.getId(); // lower id (earlier task) has higher priority | ||
}); | ||
|
||
@Override | ||
public void scheduleTask(Task task) { | ||
taskQueue.add(task); | ||
} | ||
|
||
@Override | ||
public void update(int deltaTime) { | ||
Task task = taskQueue.poll(); | ||
if (task == null) return; | ||
task.execute(deltaTime); | ||
if (!task.isComplete()) taskQueue.add(task); | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
scheduler/src/main/java/com/iluwatar/scheduler/Simulator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package com.iluwatar.scheduler; | ||
|
||
import java.beans.PropertyChangeEvent; | ||
import java.beans.PropertyChangeListener; | ||
import java.util.LinkedHashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
/** Simulate scheduler schedule tasks. */ | ||
@RequiredArgsConstructor | ||
public class Simulator implements PropertyChangeListener { | ||
private final TaskScheduler scheduler; | ||
|
||
/** Map time to tasks that need to be scheduled at that time. */ | ||
private final Map<Integer, List<Task>> tasks; | ||
|
||
private final int deltaTime; | ||
private final int simulateTime; | ||
private final LinkedHashMap<Integer, Integer> taskCompletedOrder = new LinkedHashMap<>(); | ||
private int elapsedTime = 0; | ||
|
||
public LinkedHashMap<Integer, Integer> simulate() { | ||
while (elapsedTime < simulateTime) { | ||
if (tasks.containsKey(elapsedTime)) { | ||
for (Task task : tasks.get(elapsedTime)) { | ||
task.getSupport().addPropertyChangeListener(this); | ||
scheduler.scheduleTask(task); | ||
} | ||
} | ||
scheduler.update(deltaTime); | ||
elapsedTime += deltaTime; | ||
} | ||
return taskCompletedOrder; | ||
} | ||
|
||
@Override | ||
public void propertyChange(PropertyChangeEvent evt) { | ||
if (Task.COMPLETE_PROPERTY.equals(evt.getPropertyName())) { | ||
onTaskComplete(evt); | ||
} | ||
} | ||
|
||
private void onTaskComplete(PropertyChangeEvent evt) { | ||
Task task = (Task) evt.getSource(); | ||
/* | ||
elapsedTime is updated after task dispatch complete event to simulator, | ||
so we need to add deltaTime | ||
*/ | ||
taskCompletedOrder.put(task.getId(), elapsedTime + deltaTime); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.iluwatar.scheduler; | ||
|
||
import java.beans.PropertyChangeSupport; | ||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Getter | ||
@RequiredArgsConstructor | ||
public class Task { | ||
public static final String COMPLETE_PROPERTY = "complete"; | ||
private final PropertyChangeSupport support = new PropertyChangeSupport(this); | ||
private final int id; | ||
private final int totalExecutionTime; | ||
|
||
/** The priority of the task. The higher the number, the higher the priority. */ | ||
private int priority = 0; | ||
|
||
private int elapsedTime = 0; | ||
private boolean complete = false; | ||
|
||
public Task(int id, int totalExecutionTime, int priority) { | ||
this.id = id; | ||
this.totalExecutionTime = totalExecutionTime; | ||
this.priority = priority; | ||
} | ||
|
||
public void execute(int seconds) { | ||
if (complete) throw new IllegalStateException("Task already completed"); | ||
|
||
elapsedTime += seconds; | ||
|
||
// Uncomment the following line to see the execution of tasks | ||
// System.out.printf("%d, %d/%d\n", id, elapsedTime, totalExecutionTime); | ||
|
||
if (elapsedTime >= totalExecutionTime) { | ||
complete = true; | ||
support.firePropertyChange(COMPLETE_PROPERTY, false, true); | ||
} | ||
} | ||
|
||
public int getRemainingTime() { | ||
return totalExecutionTime - elapsedTime; | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
scheduler/src/main/java/com/iluwatar/scheduler/TaskScheduler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.iluwatar.scheduler; | ||
|
||
|
||
public interface TaskScheduler { | ||
/** Add task to the scheduler */ | ||
void scheduleTask(Task task); | ||
|
||
/** Update to execute scheduled tasks */ | ||
void update(int deltaTime); | ||
} |
Oops, something went wrong.