Chapter 2 · Lesson 1

Sequential Execution

Before you can reason about many things happening at once, you need a crystal-clear picture of how a program does just one thing at a time.

One instruction after another

Every program you've ever written starts life as a sequential process: the CPU fetches one instruction, executes it, then moves to the next. There is a single point of control — often called the program counter — that marches forward step by step. Statements run top to bottom; a loop body repeats; a method call jumps away and then returns to exactly where it left off. Nothing happens "in the background." If line 7 is slow, line 8 simply waits.

This model is wonderfully easy to reason about. You can read code with your finger, tracking a single "you are here" marker, and you will never be surprised by two statements running at the same instant. That predictability is exactly what we'll later trade away — knowingly — to gain concurrency. So it pays to understand the sequential machine deeply first.

Analogy

Sequential execution is like reading a recipe out loud to yourself and doing exactly one step before starting the next: chop the onion, then heat the oil, then add the onion. You never stir and chop simultaneously. The dish gets made, but every slow step (waiting for water to boil) is dead time where you stand and do nothing else.

The call stack: how the program remembers where it is

When one method calls another, the program needs to remember where to come back to — and it needs a fresh set of local variables for the new method. Both jobs are handled by the call stack: a last-in, first-out structure of stack frames. Each call pushes a new frame holding that method's parameters, local variables, and return address. When the method finishes, its frame is popped and control returns to the caller.

Because it's a stack, the most recently called method is always the first to finish and unwind. This is also why deep or infinite recursion produces a StackOverflowError in Java: you pushed more frames than the stack had room for.

public class Factorial {
    static long fact(int n) {
        if (n <= 1) return 1;          // base case
        return n * fact(n - 1);        // recursive call
    }

    public static void main(String[] args) {
        System.out.println(fact(4));   // prints 24
    }
}

Tracing fact(4) by hand means watching the stack grow as calls are made, then shrink as each returns. The diagram below shows the stack at its deepest point, right before the base case returns.

Call stack at its deepest (evaluating fact(4)) fact(4) — waiting on fact(3) fact(3) — waiting on fact(2) fact(2) — waiting on fact(1) fact(1) — base case, returns 1 ↑ top of stack ↓ bottom (main) Each call pushes a frame; the base case at the top unwinds first, then 2·1, 3·2, 4·6 → 24.
Four frames are live at once. They return in reverse order, multiplying back up to 24.
Key idea

A single thread of execution is a single call stack plus a program counter. When we add threads later, the headline change is simple to state: each thread gets its own stack and its own program counter, but they share everything else. Hold onto that — it explains almost everything that follows in this course.

Where the sequential model runs out of road

Sequential execution is correct and simple, but it has two hard limits that modern software cannot live with:

  • It wastes idle time. When the one line of execution blocks on a slow file read or a network call, the entire program stops. The CPU sits idle even though there's other useful work it could be doing.
  • It can use only one core. A sequential program is a single stack on a single core. On a 12-core machine, eleven cores stay completely unused no matter how heavy the workload.

These aren't flaws in your code — they're inherent to the one-thing-at-a-time model. The only escape is to introduce more than one line of execution, which is precisely what concurrency is. That's where we go next.

Gotcha

"Sequential" describes the logical order, not the literal hardware. A modern CPU may reorder and pipeline instructions internally for speed — but it carefully preserves the illusion that your single thread ran in program order. That illusion quietly breaks once two threads observe each other's memory, which is a subtle topic we tackle in the memory-model sections later.

Exercises
  • By hand, trace the call stack of fact(4). Draw each frame as it's pushed, then show the values returned as each frame pops. Confirm you arrive at 24.
  • Write a small recursive function of your own (e.g. sum of a list, or Fibonacci) and trace its stack to a depth of four calls.
  • Identify one place in a program you've written where the single line of execution had to wait — and describe what else it could usefully have been doing.
✅ Self-check
What two things does a stack frame hold for a method call?

The method's local state (parameters and local variables) and the return address — where to resume in the caller once this method finishes. Each call gets its own frame.

Why does deep recursion cause a StackOverflowError?

Every unreturned call pushes another frame. The call stack has a fixed size, so enough nested calls exhaust it before any frame gets a chance to pop — and the JVM throws StackOverflowError.

Name the two fundamental limitations of a purely sequential program.

It stalls completely whenever its single line of execution blocks (wasting idle CPU time), and it can use only one core, leaving all other cores idle. Both are solved only by adding more lines of execution.

Lesson Summary
  • A sequential program has one program counter and one call stack: strictly one instruction after another.
  • The call stack pushes a frame per method call and pops it on return — last in, first out.
  • A "thread of execution" is precisely a stack plus a program counter; later, each thread gets its own.
  • The sequential model wastes idle time on blocking and can never use more than one core — the reason concurrency exists.