threading
join method
concurrency
Python programming
multithreading

What is the use of join in threading?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In Python threading, join is the method used to wait for another thread to complete. It gives you explicit control over execution order between concurrent and sequential phases. Without join, programs often behave nondeterministically because main flow can continue before worker threads finish.

Core Semantics of join

When code calls target_thread.join(), the calling thread blocks until target_thread exits, unless a timeout is provided.

python
1import threading
2import time
3
4result = []
5
6def worker():
7    time.sleep(0.2)
8    result.append("ready")
9
10t = threading.Thread(target=worker)
11t.start()
12t.join()
13
14print(result)

The print runs only after the worker has completed, so ordering is guaranteed.

Coordinating Many Threads

Typical structure in batch tasks:

  1. Start all worker threads.
  2. Let them run in parallel.
  3. Join them all.
  4. Aggregate results in one deterministic step.
python
1import threading
2
3values = []
4lock = threading.Lock()
5
6
7def compute(i):
8    local = i * i
9    with lock:
10        values.append(local)
11
12threads = []
13for i in range(8):
14    t = threading.Thread(target=compute, args=(i,))
15    t.start()
16    threads.append(t)
17
18for t in threads:
19    t.join()
20
21print(sorted(values))

This pattern avoids premature aggregation and keeps lifecycle logic readable.

Timeout Does Not Mean Cancellation

join(timeout=...) only limits wait time. It does not terminate the thread. After timeout, check is_alive() and apply cooperative cancellation.

python
1import threading
2import time
3
4stop = threading.Event()
5
6
7def background_job():
8    while not stop.is_set():
9        time.sleep(0.1)
10
11thread = threading.Thread(target=background_job)
12thread.start()
13
14thread.join(timeout=0.5)
15if thread.is_alive():
16    print("still running, requesting stop")
17    stop.set()
18    thread.join(timeout=1.0)
19
20print("shutdown complete")

Use event flags, queue sentinels, or shared atomic-like state for cooperative stop behavior.

join Versus Thread-Safe Data Access

join indicates completion, but it does not protect shared data while threads are active. Use thread-safe communication primitives for data transfer.

python
1import queue
2import threading
3
4q = queue.Queue()
5
6def producer(n):
7    for i in range(n):
8        q.put(i + 1)
9
10producer_thread = threading.Thread(target=producer, args=(5,))
11producer_thread.start()
12producer_thread.join()
13
14total = 0
15while not q.empty():
16    total += q.get()
17
18print(total)

Queue handles safe concurrent exchange. join handles phase boundary.

Placement Strategy and Performance

Joining immediately after each start serializes work and removes concurrency benefit. Start all independent threads first, then join at the latest safe point.

In UI apps or event loops, avoid long blocking joins on the main loop thread. Use non-blocking orchestration patterns where completion triggers callbacks or state transitions.

Daemon and Shutdown Considerations

Daemon threads can be terminated abruptly when the program exits. Non-daemon worker threads should usually be joined during shutdown to ensure cleanup steps complete.

A clean shutdown sequence is:

  • Signal workers to stop.
  • Join each critical worker with timeout.
  • Log any thread that remains alive for diagnostics.

This makes operational behavior predictable in production services.

Practical Alternative APIs

For many tasks, concurrent.futures.ThreadPoolExecutor provides cleaner lifecycle management than manual thread lists. Even in that model, waiting for completion is still the same idea as join, just expressed through futures. Understanding join first makes higher-level APIs easier to reason about, especially during timeout handling and graceful shutdown design.

Common Pitfalls

  • Assuming join can force-stop a blocked worker.
  • Using join as a substitute for locks or queues.
  • Joining every thread immediately and accidentally serializing the workload.
  • Blocking UI or event-loop threads with long joins.
  • Skipping joins for critical non-daemon threads during shutdown.

Summary

  • 'join waits until a target thread finishes.'
  • It enforces ordering between concurrent and sequential phases.
  • Timeout join improves liveness but requires explicit cancellation logic.
  • Data safety during execution still requires synchronization primitives.
  • Place joins deliberately to keep both correctness and concurrency.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.