Home/Blog

Back to blog

Blog

This is where I will publish my articles, notes, and ideas about the things I am building and learning.

/
English
Engineering journalJune 23, 202612 min read

Building Battle Arena: Unifying Concurrent Programming, Process Scheduling, and OOP in a Robust Simulator

This article describes the development of Battle Arena Manager, a multithreaded matchmaking and battle scheduling simulator. The project demonstrates the practical application of Operating Systems (OS) concepts—such as concurrency, mutual exclusion, cooperative synchronization, scheduling, and resource management—integrated with Object-Oriented Programming (OOP) in Java.

JavaConcurrencyOperating SystemsOOP

This article provides a detailed walkthrough of the development of the Battle Arena Manager, a multithreaded matchmaking and battle scheduling simulator. The project was designed to demonstrate the practical application of Operating Systems (OS) concepts—such as concurrency, mutual exclusion, cooperative synchronization, scheduling, and resource management—through the lens of software engineering using Object-Oriented Programming (OOP) in Java. Here, we discuss the architectural flow, concurrent dynamics, major technical challenges, and the data safety mechanisms implemented.

1. Introduction: The Matchmaking Challenge

In modern mass-scale online games like League of Legends, Valorant, or Counter-Strike, the matchmaking system is one of the most critical infrastructure components. It must handle an uninterrupted flow of hundreds or thousands of player requests per second, organize them into separate queues based on their interests, create balanced matches, and schedule these matches on limited computational resources (dedicated servers)—all without causing latency or data inconsistencies.

The Battle Arena was designed to model this dynamic. The engineering challenge here was to simulate this complex environment on a single machine, ensuring that the concurrent flow of generating player connections, pairing matches, scheduling, and graphical execution ran in parallel and free of locks (deadlocks), starvation (starvation), or duplicate data (race conditions).

2. The Simulator's Architecture

The system was structured following a Layered Architecture to separate the presentation logic from the concurrent operational logic and domain entities.

Simulator Architecture
Simulator Architecture

Each package has a well-defined responsibility:

  • app: Console entry point, where the simulation can be controlled via the terminal (Main.java).
  • model: Contains data structures. Player.java represents the user, QueueRequest.java tracks their queue entry time, BattleRequest.java is the paired match, and Metrics.java aggregates performance metrics in a thread-safe manner.
  • service: Main queue logic (MatchmakingService.java) and computational resource management and prioritization (BattleSchedulerService.java).
  • thread: All parallel logic encapsulated to run concurrently in the background.
  • view: An enriched JavaFX interface (BattleArenaUI.java) that displays resource usage graphs and updates thread logs in real time.

3. Applied Operating Systems and Concurrency Concepts

The engineering of the simulator is heavily based on operating systems dynamics:

A. Multithreading and Concurrency

The system operates via 5 types of cooperative threads:

  1. PlayerGeneratorThread: Simulates network traffic by generating new random players.
  2. MatchmakingThread: Actively monitors queues and attempts to pair available players into battles.
  3. SchedulerThread: Coordinates the start of matches in the priority queue, waiting for fictional server slot resources.
  4. BattleThread: Simulates the battle running in the background with variable duration (random based on type). It releases server resources upon completion.
  5. MonitorThread: Performs system health monitoring ('logical Garbage Collector') every 3 seconds.

B. The Producer-Consumer Pattern

We have a two-way production-consumption chain:

  • The generator thread (PlayerGeneratorThread) produces new player records and inserts them into the MatchmakingService queues. The MatchmakingThread acts as the consumer of these queues, extracting and emptying them to create BattleRequest objects.
  • The MatchmakingThread then becomes the producer of battle requests for the BattleSchedulerService. Finally, the scheduling thread (SchedulerThread) consumes these ready battles, dispatching them when server resources become free.
Producer-Consumer Diagram
Producer-Consumer Diagram

C. Mutual Exclusion (Mutex) and Thread Safety

Since the player queues and the list of pending battles are read and modified simultaneously by different parallel processes, there is a serious risk of a Race Condition. If two processes tried to remove the same player from the queue at the same time, it would cause memory corruption.

To guarantee that each critical operation on the lists occurs Atomically, we implemented synchronized blocks (synchronized) protected by a private atomic lock in MatchmakingService.java:

java
private final Object lock = new Object();

public void addPlayerToQueue(Player player, BattleType type) {
    synchronized (lock) {
        queues.get(type).add(new QueueRequest(player));
        lock.notifyAll(); // Wakes up threads in wait()
    }
}

This guarantees Mutual Exclusion: only one thread at a time can acquire the lock to read or write data in the shared queues.

D. Thread Communication (Avoiding Busy Waiting)

Searching for matches continuously in a while(true) loop without pausing would max out the computer's CPU (a harmful practice known as Busy Waiting). To mitigate this, we implemented Java's cooperative calls:

  • wait(): Execution enters a suspended state when there are not enough elements in the queue.
  • notifyAll(): The generator thread wakes up all sleeping threads whenever a new player is added.

E. Non-Preemptive Priority Scheduling with Aging

The Battle Arena scheduler manages the allocation of scarce server resources (total capacity = 10 slots). Battles have distinct costs and priorities defined in BattleType.java:

  • CASUAL_MATCH: Cost of 1 slot, Low Priority.
  • RANKED_MATCH: Cost of 2 slots, Medium Priority.
  • TOURNAMENT_MATCH: Cost of 3 slots, High Priority.

A simple priority scheduler would cause the Starvation problem: a constant stream of high-priority tournaments would prevent low-priority casual matches from acquiring server slots. To solve this fairly, we applied the Aging algorithm in BattleRequest.java:

java
public double calculatePriorityWithAging() {
    long waitSeconds = (System.currentTimeMillis() - creationTime) / 1000;
    // Increments priority by 1 unit every 5 seconds of wait time
    return this.battleType.getPriority() + (waitSeconds / 5.0);
}

Periodically, pending battles in the scheduler are dynamically reordered based on this formula. Consequently, a Casual request waiting long enough eventually overtakes a newly arrived Tournament. The system is Non-Preemptive, meaning that once started, a battle consumes its resources until the end without being forced to pause by higher-priority requests.

4. Practical Object-Oriented Programming (OOP) Concepts

The robustness and maintainability of the Battle Arena stem from a meticulous application of the core pillars of OOP:

  • Encapsulation: The aggregated metrics counting logic in Metrics.java protects its attributes from unauthorized writes. The counter uses internal synchronized modifiers to ensure that increments from multiple concurrent threads occur safely without corrupting statistics.
  • Abstraction: The JavaFX visual interface does not deal with locks, semaphores, or queue ordering. It only interacts with the high-level public methods exposed by the services, completely decoupling the graphical presentation from the concurrent operational engine.
  • Polymorphism: The system's threads are initialized using uniform instances of the Runnable interface. This allows the concurrent lifecycle of completely different objects (player generator, scheduler, and UI manager) to be managed consistently by the Java Thread framework.
  • Type Safety via Enums: The BattleType.java enum encapsulates not just constants, but domain intelligence, storing the maximum wait limits (maxWaitTime) and computational resource costs (requiredResources) for each game category.
Battle Arena JavaFX Interface
Battle Arena JavaFX Interface

5. Error Prevention and Robustness Guarantees

When designing concurrent systems, debugging is notoriously difficult due to asynchronous behavior. The project utilized defensive strategies to shield the code against critical bugs:

  1. Preventing ConcurrentModificationException:
  • The Bug: Occurs when a thread attempts to iterate over a list (for example, to print reports or check timeouts) while another thread is adding or removing elements.
  • The Solution: All collections subject to shared access are encapsulated within scopes synchronized on the same lock object. When iterating over queues in MatchmakingService.java, we do so synchronously or by creating instant temporary copies of the collections before pruning them.
  1. Mitigating Deadlocks:
  • The Bug: Occurs when two threads are blocked forever, each waiting for a resource held by the other.
  • The Solution: We adopted a Single and Well-Defined Lock policy. Instead of synchronizing operations with multiple cross-locks (one lock per queue type), we synchronize all matchmaking list accesses around a single private lock. This prevents scenarios where Thread A holds Lock X waiting for Lock Y, while Thread B holds Lock Y waiting for Lock X.
  1. Handling Concurrent Timeouts (Queue Abandons):
  • The Bug: Players exceeding the wait time limit must be removed. However, if timeout cleanups happened simultaneously with matchmaking, data could be corrupted.
  • The Solution: The MonitorThread.java calls the removal method, which executes synchronously under the protective lock. If the player is matched in the exact same millisecond, the system detects that the ticket is already consumed and safely aborts the timeout.

6. Conclusion

Developing the Battle Arena Manager proved that concurrency management in distributed systems and game infrastructures requires extreme rigor in synchronization and modeling.

The fusion of classic Operating Systems algorithms, such as Priority Scheduling with Aging, with the pillars of Object-Oriented Programming, resulted in a resilient application capable of handling simulated player traffic spikes while ensuring fairness in queue wait times, server resource stability, and clear real-time visual monitoring of dynamic interactions. You can check out the full source code on GitHub!

Building Battle Arena: Unifying Concurrent Programming, Process Scheduling, and OOP in a Robust Simulator | Renan Costa