threading
static variables
concurrency
multithreading
programming

Are static variables shared between threads?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Static variables are usually shared by all threads in the same process because the threads share the same memory space. That fact is simple, but it leads to a common mistake: shared does not mean safe. A static field gives every thread access to the same state, which is useful for caches and configuration, but dangerous for mutable counters or collections unless concurrency is handled explicitly.

Why Static Variables Are Shared

A static variable belongs to the class, not to an instance. In most runtime models, including Java and C#, all threads in one process can see the same class-level storage.

A Java example makes this obvious:

java
1public class SharedState {
2    private static int counter = 0;
3
4    public static void increment() {
5        counter++;
6    }
7
8    public static int getCounter() {
9        return counter;
10    }
11}

Every thread calling increment() touches the same counter field. There is not one copy per thread and not one copy per object.

Shared State Creates Race Conditions

The next step is the important one: counter++ is not atomic. It is a read, then a calculation, then a write. Two threads can interleave those steps and lose updates.

java
1public class RaceConditionDemo {
2    private static int counter = 0;
3
4    public static void main(String[] args) throws InterruptedException {
5        Thread t1 = new Thread(() -> {
6            for (int i = 0; i < 100_000; i++) {
7                counter++;
8            }
9        });
10
11        Thread t2 = new Thread(() -> {
12            for (int i = 0; i < 100_000; i++) {
13                counter++;
14            }
15        });
16
17        t1.start();
18        t2.start();
19        t1.join();
20        t2.join();
21
22        System.out.println(counter);
23    }
24}

You might expect 200000, but in a real run the result can be smaller because increments were lost.

How To Make Shared Static State Safe

If multiple threads write shared static data, use synchronization or a thread-safe abstraction.

java
1import java.util.concurrent.atomic.AtomicInteger;
2
3public class SafeCounter {
4    private static final AtomicInteger counter = new AtomicInteger();
5
6    public static void increment() {
7        counter.incrementAndGet();
8    }
9
10    public static int getCounter() {
11        return counter.get();
12    }
13}

AtomicInteger is a good choice for simple counters. For more complex shared state, you may need synchronized, locks, or concurrent collections.

volatile Does Not Solve Everything

A frequent misunderstanding is that volatile makes shared state “thread-safe.” It does not. volatile helps with visibility, meaning one thread sees another thread’s most recent write more reliably. But it does not make compound operations such as incrementing atomic.

So this is still unsafe for counting:

java
private static volatile int counter = 0;

If the problem is read-modify-write, use atomic operations or locking.

When You Need Per-Thread State Instead

Sometimes developers ask about static fields because they want a value accessible everywhere, but what they really need is a separate value per thread. In Java, ThreadLocal handles that.

java
1public class ThreadLocalDemo {
2    private static final ThreadLocal<Integer> requestId =
3        ThreadLocal.withInitial(() -> 0);
4
5    public static void main(String[] args) {
6        Runnable task = () -> {
7            requestId.set(requestId.get() + 1);
8            System.out.println(Thread.currentThread().getName() + " -> " + requestId.get());
9        };
10
11        new Thread(task, "A").start();
12        new Thread(task, "B").start();
13    }
14}

The ThreadLocal reference is static, but the contained value is separate for each thread. That is a very different design from a shared static variable.

Common Pitfalls

  • Assuming a shared static field is automatically thread-safe.
  • Using volatile for operations that need atomicity.
  • Forgetting that mutable static collections also need synchronization discipline.
  • Using static state when the real requirement is per-thread or per-request data.

Summary

  • Static variables are generally shared between threads in the same process.
  • Shared visibility does not make writes safe.
  • Mutable shared static state needs locking, atomics, or concurrent data structures.
  • 'volatile helps with visibility, not compound updates.'
  • Use ThreadLocal when you need one logical value per thread instead of one shared value.

Course illustration
Course illustration

All Rights Reserved.