In modern software engineering, designing systems that can handle multiple tasks simultaneously is no longer a luxury; it is a necessity. However, modeling concurrency using traditional object-oriented diagrams can be challenging, as standard UML classes are inherently passive—they wait to be called into action and cannot independently initiate activity.
Enter Active Classes. Active classes are the primary tool in the Unified Modeling Language (UML) for modeling system concurrency. By representing independent flows of control, they allow architects and developers to bridge the gap between static structural design and dynamic runtime behavior. This guide explores how active classes function, the key concepts surrounding them, and how to effectively use them to model complex concurrent systems.

Before diving into the architectural applications, it is essential to understand the foundational terminology and mechanics of active classes in UML:
<<process>> and <<thread>>.{sequential}, {guarded}, {concurrent}).Active classes allow system designers to give a concrete, visual name to each independent flow of control within a system. UML distinguishes between two standard stereotypes of active classes to map directly to operating system concepts:
<<process>>): These represent heavyweight flows of control. They execute in independent address spaces and are typically managed directly by the operating system. In a UML model, a process stereotype implies a higher level of isolation and resource overhead.<<thread>>): These represent lightweight flows of control. They usually run within the shared address space of an enclosing process. Threads are ideal for modeling concurrent tasks that need to share memory and resources efficiently.By explicitly modeling these, architects can make informed decisions about resource allocation, memory isolation, and execution overhead early in the design phase.
In a concurrent system, multiple events occur simultaneously. Active classes provide the mechanism to visualize these parallel executions, particularly within interaction diagrams (like Sequence or Communication diagrams).
Modeling concurrency is not just about creating parallel flows; it is about managing how those flows communicate and maintain data integrity. Active classes help define these interactions through specific semantics:
When multiple flows of control attempt to enter an active object simultaneously, the object must behave predictably. UML allows you to attach concurrency semantics to operations:
{sequential}: Only one flow of control is allowed to execute the operation at a time. Callers must coordinate externally, or the system may risk race conditions.{guarded}: The object itself guarantees data integrity by automatically sequentializing all calls to its operations. (Often implemented via mutexes or locks).{concurrent}: The operation is treated as strictly atomic, allowing multiple concurrent calls to proceed simultaneously without corrupting state.In the "4+1" architectural view model, active classes are the focal point of the Process View. While other views might focus on code structure or user interactions, the Process View addresses critical non-functional requirements:
To solidify these concepts, below are two PlantUML examples demonstrating how to model active classes in both structural and behavioral diagrams.
This diagram illustrates an Order Processing system. It shows a <<process>> managing multiple <<thread>> workers, and demonstrates how to apply concurrency semantics to specific operations.

@startuml
skinparam classAttributeIconSize 0
skinparam monochrome true
class "OrderProcessor" <<process>> {
+ startProcessing()
+ routeOrder() {guarded}
}
class "PaymentWorker" <<thread>> {
+ chargeCard() {concurrent}
+ refundCard() {guarded}
}
class "InventoryWorker" <<thread>> {
+ reserveStock() {sequential}
+ updateCatalog() {concurrent}
}
' The process owns/creates the threads
OrderProcessor "1" *-- "many" PaymentWorker : manages >
OrderProcessor "1" *-- "many" InventoryWorker : manages >
note bottom of OrderProcessor
**Process View:**
Heavyweight flow managing
the overall lifecycle.
end note
note bottom of PaymentWorker
**Concurrency:**
chargeCard is {concurrent}
because it is stateless/atomic.
refundCard is {guarded} to
prevent double-refunding.
end note
@enduml
This diagram demonstrates how active objects interact using both Asynchronous (Mailbox) and Synchronous (Rendezvous) communication semantics.

@startuml
skinparam monochrome true
participant "API Gateway\n<>" as GW
participant "Audit Logger\n<>" as Log
participant "Payment Service\n<>" as Pay
== 1. Asynchronous Messaging (Mailbox Semantics) ==
GW ->> Log : logRequest(transactionId)
note right of GW
Gateway sends the message
and continues immediately.
Logger processes it in the
background.
end note
GW -> GW : parseNextRequest()
== 2. Synchronous Rendezvous ==
GW -> Pay : processPayment(amount)
note right of GW
Gateway blocks and waits
for the Payment Service
to complete the transaction.
end note
Pay --> GW : returnReceipt()
GW --> GW : sendResponseToClient()
@enduml
Active classes are an indispensable tool in the UML toolkit for anyone designing complex, concurrent, or distributed systems. By elevating processes and threads to first-class citizens in your design models, you move beyond simply describing what the system is built of, to describing how it behaves under the hood.
Whether you are mapping out the heavyweight processes of a microservices architecture, defining the thread-safe guards of a high-frequency trading engine, or simply trying to prevent a deadlock in a multi-threaded application, active classes provide the vocabulary and visual framework necessary to reason about concurrency. Mastering them ensures that your system's most critical runtime behaviors are designed intentionally, rather than discovered accidentally in production.