Do threads share local variables?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In normal threaded execution, local variables are not shared between threads. Each thread has its own call stack, so each invocation of a function gets its own separate set of local variables. The confusion usually comes from the fact that a local variable can still point to a shared object.
The Basic Rule
A local variable lives inside a specific stack frame. Since each thread has its own stack, thread A cannot directly read or overwrite thread B's local variable just because both threads are running the same function.
That means this kind of state is isolated by default:
Each thread prints its own counter value. The variable is local to that thread's execution of work().
What Actually Gets Shared
The tricky part is references. A local variable itself is private to the thread, but it may refer to an object stored on the heap, and heap objects can be shared.
Here, the parameter counter is a local variable in each thread. But both local variables point to the same Counter object. The threads are not sharing the local variable slot; they are sharing the heap object referenced by that slot.
Stack Data Versus Heap Data
This is the mental model that avoids most mistakes:
- local variables are usually stored in a thread's stack frame
- objects created with
newtypically live on the heap - stacks are private per thread
- heap objects may be shared if more than one thread can reach them
Once you understand that separation, many concurrency questions become easier.
Parameters Follow the Same Rule
Function parameters are also local variables. If two threads call the same method, each call gets its own parameter variables. Again, the only question is whether those parameters point to the same shared objects.
In this Python example, local_count is private to each thread, but items refers to the same list object, so the list is shared.
Thread-Local Storage Is a Different Feature
Some languages also provide thread-local storage, which creates data that behaves like global state but is isolated per thread. That is different from an ordinary local variable because its lifetime is tied to the thread rather than to a single function call.
Do not mix up thread-local storage with stack locals. They solve different problems.
Closures and Async Can Blur the Picture
A variable that looks local in source code may actually be captured by a closure or stored inside an object. Once that happens, the storage location and sharing behavior may change.
That is why concurrency bugs often appear in lambdas, callbacks, and mutable captured state. The variable name still looks small and local, but the underlying object may no longer be thread-private.
Common Pitfalls
The most common mistake is saying "locals are never shared" without considering shared objects referenced by locals. The variable slot is private, but the data behind the reference may not be.
Another mistake is assuming function parameters are copied deeply. In most languages, object references are passed, so multiple threads can easily end up mutating the same object.
Finally, even when locals are thread-private, updating shared state through them still requires synchronization or other safe concurrency patterns.
Summary
- Ordinary local variables are not shared across threads.
- Each thread gets its own stack and its own stack frames.
- A local variable can still point to a shared heap object.
- Parameters follow the same rule as other locals.
- Concurrency bugs usually come from shared objects, not from the local variables themselves.

