ReviseAlgo Logo

UML Diagrams

Activity Diagram

Model procedural logic, business workflows, and concurrent execution paths

Last Updated: June 26, 2026 23 min read

A UML Activity Diagram is a behavioral diagram that models the flow of control or data within a system. It functions as an advanced object-oriented flowchart, defining sequential, branching, and concurrent execution paths. In Low-Level Design, activity diagrams visualize complex operational algorithms, business processes, and parallel processing flows.

1. Learning Objectives

  • Identify activity diagram symbols: Action, Initial, Decision, Fork, Join, and Activity Final nodes.
  • Employ Swimlanes (Partitions) to map activity ownership to system components.
  • Differentiate branching controls (Decision/Merge) from concurrency controls (Fork/Join).
  • Translate activity diagram concurrent paths directly into asynchronous thread structures.
  • Implement multi-threaded, parallel order processing in Java, Python, and C++.

2. Problem Statement

Flowcharts work well for simple, single-threaded procedures. However, enterprise software relies on complex workflows containing parallel activities (like reserving stock and charging credit cards concurrently). Building these systems without modeling the activity flow leads to:

  • Thread Race Conditions: Spawning asynchronous tasks that write to the same shared objects in a chaotic order, causing data corruption.
  • Uncoordinated Joins: Proceeding to confirm an order before the credit card transaction completes, allowing unpaid orders to slip through.
  • Monolithic Responsibilities: Designing a service class that handles payment, inventory updates, and notifications internally instead of dividing responsibilities among distinct modules.

3. Real-world Analogy

Think of the Airport Passenger Boarding Process:

  • The Analogy: A traveler enters the airport (Initial Node) and checks in. Next, they reach a decision point: do they have luggage? If yes, they drop their bags; if no, they proceed. At the security checkpoint, a Fork occurs: the passenger walks through the body scanner while their hand luggage goes through the bag scanner. Both actions run in parallel.
  • The Join: After security, the passenger collects their bags (Join). They cannot board the plane until both they and their bags have cleared security. Once boarded, the journey begins (Activity Final Node).

4. Theory (Activity Diagram Symbols & Partitions)

An Activity Diagram consists of Action Nodes (representing operations) linked by Control Flows (solid arrows representing execution order), organized into vertical or horizontal compartments called Swimlanes.

Diagram Symbols

  • Initial Node (●): A solid circle indicating where the workflow starts.
  • Action Node: A rounded rectangle representing a task or step in the process (e.g. Reserve Stock).
  • Decision Node (◇): A diamond shape with one incoming flow and multiple outgoing flows labeled with guard conditions (e.g. [in stock] vs [out of stock]). Only one branch is executed.
  • Merge Node (◇): A diamond shape that brings multiple alternative flows back together into a single sequential path.
  • Fork Node: A thick black horizontal or vertical bar with one incoming flow and multiple outgoing flows. It initiates parallel (concurrent) activities.
  • Join Node: A thick black horizontal or vertical bar with multiple incoming flows and one outgoing flow. It acts as a synchronization barrier, waiting for all parallel tasks to complete before moving forward.
  • Activity Final Node (◉): A bullseye circle indicating the end of the workflow.

Swimlanes (Partitions)

Swimlanes partition the diagram into columns or rows representing the actors, classes, or business departments responsible for executing each action. They organize the workflow visually, making it easy to map each action to a specific component.

5. Visual Diagrams (Order Processing Workflow)

The diagram below uses swimlanes to partition responsibilities for an order fulfillment process. It features parallel execution (Fork/Join) to handle payment processing and inventory reservation concurrently:

6. Syntax Explanation

UML activity controls map directly to programming language statements and concurrency patterns:

  • Decision Node (◇): Implemented via standard if-else checks: if (stockAvailable) { ... } else { ... }.
  • Fork/Join (Concurrency): Implemented using thread pool dispatchers and futures join barriers. In Java, we use CompletableFuture.allOf(). In Python, we use asyncio.gather(). In C++, we use std::async() along with future wait hooks.
  • Swimlanes: Map directly to distinct class dependencies injected into the coordinator service class (e.g. OrderService has references to InventoryService and PaymentService).

7. Step-by-Step Creation

  1. Establish Swimlanes: Identify the main components or classes in the system and draw vertical swimlanes for each.
  2. Place Initial Node: Place a solid green start circle () in the column of the actor or system component that triggers the process.
  3. Add Sequential Actions: Add action boxes representing steps in the workflow, placing each in the column of the component responsible for it. Link them with solid control flow arrows.
  4. Introduce Branching (Decision Nodes): Add diamond decision shapes where conditional branching occurs. Label the outgoing arrows with guard conditions (e.g. [valid] vs [invalid]).
  5. Introduce Concurrency (Fork/Join): Draw a thick black line (Fork) where activities can run in parallel. Add a matching thick black line (Join) to synchronize the parallel paths before moving forward.
  6. Connect Final Node: Link the final action to a bullseye end circle () to complete the diagram.

8. Complete Code (Mini Project)

9. Code Walkthrough

Let's trace how the concurrent activity diagram maps directly to multi-threaded execution:

  • In OrderService.processOrder(), the execution starts sequentially. It prints the initial order creation log (which corresponds to the Create Order Record action node).
  • To implement the Fork Node, the code spawns two asynchronous tasks (Java's CompletableFuture.supplyAsync, Python's coroutines, or C++'s std::async). These tasks run concurrently on separate threads (e.g. Forking execution).
  • The tasks execute their respective service methods: reserveStock() and processPayment() (the parallel action nodes).
  • To implement the Join Node, the coordinator service blocks execution until both futures return values (Java's allOf() or C++'s future.get()). This acts as a synchronization barrier, ensuring both tasks finish before proceeding.
  • Once synchronized, the service proceeds to the final action (Confirm Order) and prints the end message, ending execution.

10. Execution Flow

  1. Initial Step: Main execution triggers order processing.
  2. Forking: The system retrieves threads from a thread pool to run stock reservation and payment checks in parallel.
  3. Sync Point (Join): The calling thread blocks at the join barrier. If one task finishes early, it waits for the other.
  4. Order Confirmation: Once both tasks return successfully, the flow transitions to order confirmation and terminates.

11. Internal Working (Fork/Join Barriers)

At the operating system and hardware level, Fork and Join nodes correspond to thread synchronization primitives:

  • Fork Primitive: When a Fork is reached, the main thread schedules tasks onto a queue managed by an OS thread pool (e.g., Common ForkJoinPool in Java). Worker threads pick up these tasks, pushing new stack frames onto their respective threads.
  • Join Primitive: A Join node acts as a Barrier Synchronization primitive. When a thread reaches a join barrier, it checks the status of other tasks. If they are not finished, the thread yields or blocks (sleeping in the OS scheduler), avoiding CPU polling waste. Once all tasks complete, the barrier is released, waking the coordinator thread to continue.

12. Complexity Analysis

  • Time Complexity: $\max(T_1, T_2)$ where $T_1$ and $T_2$ are the execution times of the parallel tasks, plus a small synchronization overhead. Running tasks in parallel is faster than running them sequentially ($T_1 + T_2$).
  • Space Complexity: $O(P)$ memory overhead, where $P$ is the number of spawned threads. Spawning threads requires stack frame allocations, meaning parallel execution consumes more memory than single-threaded execution.

13. Best Practices

  • Always pair Forks and Joins: Every Fork bar that splits execution should have a matching Join bar to synchronize the flows. Leaving parallel flows unsynchronized can cause race conditions.
  • Use Swimlanes to partition logic: Arrange actions into swimlanes representing distinct classes or components to clarify boundaries and responsibilities.
  • Avoid deep nesting of decisions: Keep decision diamonds simple. If a decision path is too complex, extract it into a separate sub-activity diagram.

14. Common Mistakes

  • Confusing Decision Nodes with Fork Nodes: Using a decision diamond to split execution into parallel tasks. A decision diamond executes *only one* path, whereas a fork bar executes *all* paths concurrently.
  • Missing guard conditions: Leaving decision arrows unlabeled. Always document the guard condition (e.g. [success]) for each branch.
  • Swimlane pollution: Placing actions in the wrong swimlane columns, obscuring which class is responsible for the task.

15. Interview Questions

Q: What is the difference between a Decision Node and a Fork Node?
Answer: A Decision Node (diamond) represents a conditional branch where only one path is selected based on a guard condition. A Fork Node (thick black bar) splits execution into multiple concurrent paths that run in parallel.
Q: What is the purpose of a Join Node in a concurrent workflow?
Answer: A Join Node acts as a synchronization barrier. It pauses the workflow execution until all parallel incoming flows have completed, preventing down-stream actions from executing prematurely.
Q: What are Swimlanes in an Activity Diagram, and why are they used?
Answer: Swimlanes are vertical or horizontal partitions representing distinct actors, classes, or services. They organize actions visually to show which component is responsible for executing each task.

16. Practice Exercises

  • Easy: Draw an Activity Diagram representing a User Login workflow. Include decision branches for valid and invalid credentials.
  • Medium: Draw an Activity Diagram representing a Library Book Return process. Use swimlanes for Patron and Librarian, including checking the book's condition and calculating fines for late returns.
  • Hard: Design an Activity Diagram for a Ride-Sharing pickup workflow. Include parallel paths for calculating the optimal route and matching drivers, decision diamonds for passenger confirmation, and swimlanes for Passenger, Driver, and Server.

17. Challenge Problem

Design an Activity Diagram for a Distributed Saga Transaction in a microservice-based E-Commerce system. The workflow involves reserving stock (Inventory Service), charging payment (Payment Service), and booking delivery (Logistics Service). If any service fails, the system must trigger compensatory rollback actions (e.g. refunding payment if logistics fails). Draw the diagram showing all success, failure, and rollback branches. Write the coordination logic in Java, Python, or C++ using asynchronous futures and try-catch rollback hooks.

18. Summary

  • Activity Diagrams visualize procedural workflows, operational algorithms, and business processes.
  • Key symbols include Start/End nodes, Action rounded rectangles, and diamond Decisions.
  • Fork and Join bars split and synchronize parallel execution flows.
  • Swimlanes partition actions based on the component or actor responsible for executing them.

19. Cheat Sheet

Element Visual Symbol Flow Control Equivalent Concurrency Action
Decision Node Diamond (◇) if-else conditional block Single thread path selection
Fork Node Thick horizontal/vertical bar Spawning tasks to thread pools Splits execution into parallel paths
Join Node Thick horizontal/vertical bar Synchronization barrier (futures get) Blocks until all parallel paths complete
Swimlane Vertical or horizontal column Class or service assignment Defines component responsibility boundaries

20. Quiz

1. Which symbol is used to represent parallel branching (concurrency) in an Activity Diagram?

A) Diamond shape
B) Thick horizontal or vertical bar (Correct)
C) Bullseye circle

2. What is the difference between a Decision Node and a Fork Node?

A) A Decision executes one path; a Fork splits flow into multiple concurrent paths (Correct)
B) A Decision executes all paths; a Fork executes only one path
C) A Decision represents variables; a Fork represents loops

3. What does a Join Node do in a concurrent flow?

A) It joins class attributes into a single database column
B) It acts as a synchronization barrier, waiting for all parallel paths to complete before continuing (Correct)
C) It terminates the entire workflow

4. What represent vertical or horizontal divisions showing responsibility for actions?

A) Branches
B) Swimlanes / Partitions (Correct)
C) Guards

5. In Java, what API is commonly used to implement a Fork/Join barrier in code?

A) ArrayList
B) HashMap
C) CompletableFuture / ForkJoinPool (Correct)

6. What is the symbol for the Initial Node in an Activity Diagram?

A) A solid circle (Correct)
B) A bullseye circle
C) A diamond shape

7. What is the symbol for the Activity Final Node?

A) A solid circle
B) A bullseye circle (Correct)
C) A thick horizontal bar

8. Can an Activity Diagram have multiple start nodes?

A) No, it must have exactly one start node (Correct)
B) Yes, one for each swimlane column
C) Only if they represent databases

9. In C++, what concurrency component is used to execute tasks in parallel?

A) std::vector
B) std::async / std::future (Correct)
C) std::unique_ptr

10. What is a common mistake when drawing activity diagrams?

A) Placing actions in swimlanes
B) Confusing Decision diamonds with Fork bars, executing parallel tasks out of sequence (Correct)
C) Using join synchronization barriers

21. Next Lesson Preview

Congratulations! You have completed all UML Diagram lessons. In the next module, we will dive into Creational Design Patterns, starting with the Singleton Pattern, to learn how to manage class instantiations cleanly!