Modeling Discrete Event Simulation (DES) in Java
Discrete Event Simulation (DES) is a powerful method for modeling the behavior and performance of complex systems over time. This article describes how to implement a DES framework in Java to manage multiple entities, resources, and events efficiently.
What is Discrete Event Simulation?
DES models a system as it evolves through discrete events. Each event occurs at a specific time and represents a change in the system’s state. Examples include the arrival of a customer at a queue, the start of a process, or the release of a resource.
Core Components of the DES Framework
- Event Class: Represents an event in the system with a scheduled time, an associated entity, and an action to execute.
public class Event implements Comparable<Event> {
public final int time;
public final Entity entity;
public final Runnable action;
public Event(int time, Entity entity, Runnable action) {
this.time = Util.validateNotNegative(time);
this.entity = Objects.requireNonNull(entity);
this.action = Objects.requireNonNull(action);
}
@Override
public int compareTo(Event other) {
return Integer.compare(this.time, other.time);
}
}
- Entity Class: Represents an individual item or process interacting with the system. Each entity has a name and an interval (the time it spends using a resource).
public class Entity {
public final String name;
public final int delay;
public Entity(String name, int delay) {
this.name = Objects.requireNonNull(name);
this.delay = Util.validateNotNegative(delay);
}
}
- Resource Class: Models a resource with limited capacity. It includes a queue for waiting entities and tracks active entities currently using the resource.
public class Resource {
public final String name;
public final int capacity;
private final List<Entity> activeEntities = new ArrayList<>();
private final Queue<Runnable> waiting = new LinkedList<>();
private final TimeRange timeRange;
public Resource(String name, int capacity, TimeRange timeRange) {
this.name = Objects.requireNonNull(name);
this.capacity = Util.validateIsPositive(capacity);
this.timeRange = Objects.requireNonNull(timeRange);
}
public boolean allocate(Entity entity) {
if (activeEntities.size() < capacity) {
activeEntities.add(entity);
return true;
} else {
return false;
}
}
public Entity release() {
if (!activeEntities.isEmpty()) {
Entity entity = activeEntities.remove(0);
if (!waiting.isEmpty()) {
waiting.poll().run();
}
return entity;
}
return null;
}
public void notifyCapacity(Runnable runnable) {
waiting.offer(runnable);
}
public int processingTime() {
int start = timeRange.start;
int end = timeRange.end;
if (start == end) {
return start;
}
return end + (int) (Math.random() * ((end - start) + 1));
}
}
- Simulation Class: Manages the global clock, event scheduling, and resources. The
processEntitymethod handles resource allocation and scheduling of subsequent events.
public class Simulation {
private final PriorityQueue<Event> eventQueue = new PriorityQueue<>();
private final Map<String, Resource> resources = new HashMap<>();
private int globalClock = 0;
public void addResource(Resource resource) {
resources.put(resource.name, Objects.requireNonNull(resource));
}
private void scheduleEvent(int time, Entity entity, Runnable action) {
Event event = new Event(time, entity, action);
eventQueue.add(event);
}
public void run(int maxTime) {
while (!eventQueue.isEmpty() && globalClock <= maxTime) {
Event event = eventQueue.poll();
globalClock = event.time;
event.action.run();
}
}
public void processEntity(Entity entity, List<String> resourceNames) {
if (resourceNames.isEmpty()) {
return;
}
String currentResourceName = resourceNames.get(0);
Resource resource = resources.get(currentResourceName);
Runnable action = () -> {
if (resource.allocate(entity)) {
System.out.println(globalClock + ": " + entity.name + " started using " + resource.name);
scheduleEvent(calculateTime(entity, resource), entity, () -> {
releaseResource(currentResourceName);
processEntity(entity, resourceNames.subList(1, resourceNames.size()));
});
} else {
System.out.println(globalClock + ": " + entity.name + " queued for " + resource.name);
resource.notifyCapacity(() -> scheduleEvent(globalClock, entity, () -> processEntity(entity, resourceNames)));
}
};
scheduleEvent(globalClock, entity, action);
}
private int calculateTime(Entity entity, Resource resource) {
return globalClock + entity.delay + resource.processingTime();
}
private void releaseResource(String resourceName) {
Resource resource = resources.get(resourceName);
Entity releasedEntity = resource.release();
if (releasedEntity != null) {
System.out.println(globalClock + ": " + releasedEntity.name + " finished using " + resource.name);
}
}
}
Main Simulation Logic
The Main class sets up the simulation, defines resources and entities, and runs the simulation for a fixed duration.
public class Des4jApplication {
public static void main(String[] args) {
Simulation sim = new Simulation();
Resource resource1 = new Resource("Resource1", 2, TimeRange.of(3, 5));
Resource resource2 = new Resource("Resource2", 1, TimeRange.of(7));
sim.addResource(resource1);
sim.addResource(resource2);
Entity entity1 = new Entity("Entity1", 1);
Entity entity2 = new Entity("Entity2", 2);
Entity entity3 = new Entity("Entity3", 1);
sim.processEntity(entity1, Arrays.asList("Resource1", "Resource2"));
sim.processEntity(entity2, Arrays.asList("Resource1", "Resource2"));
sim.processEntity(entity3, Arrays.asList("Resource2", "Resource1"));
sim.run(50);
}
}
Example Output
When executed, the simulation will produce output similar to the following:
0: Entity1 started using Resource1
0: Entity3 started using Resource2
0: Entity2 started using Resource1
7: Entity1 finished using Resource1
7: Entity1 queued for Resource2
8: Entity3 finished using Resource2
8: Entity3 started using Resource1
8: Entity1 started using Resource2
9: Entity2 finished using Resource1
9: Entity2 queued for Resource2
14: Entity3 finished using Resource1
16: Entity1 finished using Resource2
16: Entity2 started using Resource2
25: Entity2 finished using Resource2
The code for this article found here
Leave a comment