A somewhat clumsy ascii-art to demonstrate the mechanism: The join() is presumably called by the main-thread. It could also be called by another thread, but would needlessly complicate the diagram.

join-calling should be placed in the track of the main-thread, but to express thread-relation and keep it as simple as possible, I choose to place it in the child-thread instead.

    without join:
    +---+---+------------------                     main-thread
        |   |
        |   +...........                            child-thread(short)
        +..................................         child-thread(long)
    
    with join
    +---+---+------------------***********+###      main-thread
        |   |                             |
        |   +...........join()            |         child-thread(short)
        +......................join()......         child-thread(long)

    with join and daemon thread
    +-+--+---+------------------***********+###     parent-thread
      |  |   |                             |
      |  |   +...........join()            |        child-thread(short)
      |  +......................join()......        child-thread(long)
      +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,     child-thread(long + daemonized)

    '-' main-thread/parent-thread/main-program execution
    '.' child-thread execution
    '#' optional parent-thread execution after join()-blocked parent-thread could 
        continue
    '*' main-thread 'sleeping' in join-method, waiting for child-thread to finish
    ',' daemonized thread - 'ignores' lifetime of other threads;
        terminates when main-programs exits; is normally meant for 
        join-independent tasks

So the reason you don't see any changes is because your main-thread does nothing after your join. You could say join is (only) relevant for the execution-flow of the main-thread.

If, for example, you want to concurrently download a bunch of pages to concatenate them into a single large page, you may start concurrent downloads using threads, but need to wait until the last page/thread is finished before you start assembling a single page out of many. That's when you use join().

Answer from Don Question on Stack Overflow
Top answer
1 of 12
424

A somewhat clumsy ascii-art to demonstrate the mechanism: The join() is presumably called by the main-thread. It could also be called by another thread, but would needlessly complicate the diagram.

join-calling should be placed in the track of the main-thread, but to express thread-relation and keep it as simple as possible, I choose to place it in the child-thread instead.

    without join:
    +---+---+------------------                     main-thread
        |   |
        |   +...........                            child-thread(short)
        +..................................         child-thread(long)
    
    with join
    +---+---+------------------***********+###      main-thread
        |   |                             |
        |   +...........join()            |         child-thread(short)
        +......................join()......         child-thread(long)

    with join and daemon thread
    +-+--+---+------------------***********+###     parent-thread
      |  |   |                             |
      |  |   +...........join()            |        child-thread(short)
      |  +......................join()......        child-thread(long)
      +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,     child-thread(long + daemonized)

    '-' main-thread/parent-thread/main-program execution
    '.' child-thread execution
    '#' optional parent-thread execution after join()-blocked parent-thread could 
        continue
    '*' main-thread 'sleeping' in join-method, waiting for child-thread to finish
    ',' daemonized thread - 'ignores' lifetime of other threads;
        terminates when main-programs exits; is normally meant for 
        join-independent tasks

So the reason you don't see any changes is because your main-thread does nothing after your join. You could say join is (only) relevant for the execution-flow of the main-thread.

If, for example, you want to concurrently download a bunch of pages to concatenate them into a single large page, you may start concurrent downloads using threads, but need to wait until the last page/thread is finished before you start assembling a single page out of many. That's when you use join().

2 of 12
101

Straight from the docs

join([timeout]) Wait until the thread terminates. This blocks the calling thread until the thread whose join() method is called terminates – either normally or through an unhandled exception – or until the optional timeout occurs.

This means that the main thread which spawns t and d, waits for t to finish until it finishes.

Depending on the logic your program employs, you may want to wait until a thread finishes before your main thread continues.

Also from the docs:

A thread can be flagged as a “daemon thread”. The significance of this flag is that the entire Python program exits when only daemon threads are left.

A simple example, say we have this:

def non_daemon():
    time.sleep(5)
    print 'Test non-daemon'

t = threading.Thread(name='non-daemon', target=non_daemon)

t.start()

Which finishes with:

print 'Test one'
t.join()
print 'Test two'

This will output:

Test one
Test non-daemon
Test two

Here the master thread explicitly waits for the t thread to finish until it calls print the second time.

Alternatively if we had this:

print 'Test one'
print 'Test two'
t.join()

We'll get this output:

Test one
Test two
Test non-daemon

Here we do our job in the main thread and then we wait for the t thread to finish. In this case we might even remove the explicit joining t.join() and the program will implicitly wait for t to finish.

🌐
Codecademy
codecademy.com › docs › python › threading › .join()
Python | Threading | .join() | Codecademy
June 3, 2022 - The second thread, thread_B, cannot start before thread_A is finished due to .join(). In the example below, the .join() method allows the last print() statement to appear at the expected time in the program’s flow of execution: ... Looking for an introduction to the theory behind programming? Master Python while learning data structures, algorithms, and more!
🌐
Tutorialspoint
tutorialspoint.com › python › python_joining_threads.htm
Python - Joining the Threads
from threading import Thread from ... i) sleep(0.1) # Create thread objects thread1 = Thread(target=my_function_1, args=(5,)) thread2 = Thread(target=my_function_2, args=(3,)) # Start the first thread and wait for it to complete ...
🌐
Bogotobogo
bogotobogo.com › python › Multithread › python_multithreading_Daemon_join_method_threads.php
Python Multithreading Tutorial: daemon threads & join method - 2020
In our sample, join() blocks the calling thread (main thread) until the threads (d / t) whose join() method is called is terminated - either normally or through an unhandled exception - or until the optional timeout occurs.
🌐
Python
docs.python.org › 3 › library › threading.html
threading — Thread-based parallelism — Python 3.14.4 ...
When the timeout argument is present and not None, it should be a floating-point number specifying a timeout for the operation in seconds (or fractions thereof). As join() always returns None, you must call is_alive() after join() to decide whether a timeout happened – if the thread is still alive, the join() call timed out.
🌐
Pythontic
pythontic.com › multithreading › thread › join
Python Thread - join() method | Pythontic.com
When join() method is invoked, the calling thread is blocked till the thread object on which it was called is terminated. For example, when the join() is invoked from a main thread, the main thread waits till the child thread on which join is invoked exits.
🌐
Delft Stack
delftstack.com › home › howto › python › join threads in python
How to Join Threads in Python | Delft Stack
March 11, 2025 - This tutorial demonstrates how to use the join method with threads in Python. Learn the significance of the join method, how to manage multiple threads, and handle exceptions effectively. Enhance your Python programming skills with practical examples and clear explanations. Perfect for beginners ...
🌐
Super Fast Python
superfastpython.com › home › tutorials › how to join a thread in python
How to Join a Thread in Python - Super Fast Python
September 11, 2022 - Find relief, download my FREE Python Concurrency Mind Maps · We can join a thread and use a timeout. A timeout allows the current thread to stop waiting for the target thread to timeout after a fixed number of seconds. We can update the first example so that the target thread takes longer to execute, in this case five seconds.
Find elsewhere
🌐
IncludeHelp
includehelp.com › python › thread-join-method-with-example.aspx
Python Thread join() Method with Example
April 24, 2023 - In the initial three Thread objects, we first created a thread and waited for it to execute and then joined to the main thread. So they are printed in the same order as called. In the next three Thread objects, they are running simultaneously, so they get printed according to the time they ...
🌐
Real Python
realpython.com › intro-to-python-threading
An Intro to Threading in Python – Real Python
August 5, 2024 - If you look at the source for Python threading, you’ll see that threading._shutdown() walks through all of the running threads and calls .join() on every one that does not have the daemon flag set.
🌐
Oregoom
oregoom.com › home › joining threads in python
▷ Joining Threads in Python - Oregoom.com
October 29, 2024 - import threading import time def ... execution until the thread finishes running. In this example, the thread waits 2 seconds before finishing, and only after this does the main program continue....
🌐
GeeksforGeeks
geeksforgeeks.org › python › joining-threads-in-python
Joining Threads in Python - GeeksforGeeks
June 22, 2020 - The global variables (stored in the heap) and the program codes are shared among all the threads. On invoking the join() method, the calling thread gets blocked until the thread object (on which the thread is called) gets terminated.
Top answer
1 of 3
68

A call to thread1.join() blocks the thread in which you're making the call, until thread1 is finished. It's like wait_until_finished(thread1).

For example:

import time

def printer():
    for _ in range(3):
        time.sleep(1.0)
        print "hello"

thread = Thread(target=printer)
thread.start()
thread.join()
print "goodbye"

prints

hello
hello
hello
goodbye

—without the .join() call, goodbye would come first and then 3 * hello.

Also, note that threads in Python do not provide any additional performance (in terms of CPU processing power) because of a thing called the Global Interpreter Lock, so while they are useful for spawning off potentially blocking (e.g. IO, network) and time consuming tasks (e.g. number crunching) to keep the main thread free for other tasks, they do not allow you to leverage multiple cores or CPUs; for that, look at multiprocessing which uses subprocesses but exposes an API equivalent to that of threading.

PLUG: ...and it is also for the above reason that, if you're interested in concurrency, you might also want to look into a fine library called Gevent, which essentially just makes threading much easier to use, much faster (when you have many concurrent activities) and less prone to concurrency related bugs, while allowing you to keep coding the same way as with "real" threads. Also Twisted, Eventlet, Tornado and many others, are either equivalent or comparable. Furthermore, in any case, I'd strongly suggest reading these classics:

  • Generator Tricks for Systems Programmers
  • A Curious Course on Coroutines and Concurrency
2 of 3
5

I modified the code so that you will understand how exactly join works. so run this code with comments and without comments and observe the output for both.

val = 0

def increment(msg,sleep_time):
   global val 
   print "Inside increment"
   for x in range(10):
       val += 1
       print "%s : %d\n" % (msg,val)
       time.sleep(sleep_time)

thread1 = threading.Thread(target=increment, args=("thread_01",0.5))
thread2 = threading.Thread(target=increment, args=("thread_02",1))
thread1.start()
#thread1.join()
thread2.start()
#thread2.join()
🌐
Dkharazi
dkharazi.github.io › notes › py › threading › join
t.join
>>> t = Thread(target=sleepy) >>> t.start() thread started >>> t.join() # haults any further execution thread done # until thread finishes >>> print('main done') # now we can print main done · Python Essential References · Documentation for Threading API · Examples of Threading in Python ·
🌐
Medium
medium.com › @e.ahmadi › when-and-why-we-should-use-join-in-multi-threading-5e7fa331c05d
When and why we should use Join in multi-threading | by Ehsan Ahmadi | Medium
September 3, 2021 - For example, change the code like this: ... thread start thread finished process exitedExecuting — /Users/Ehsan/test_threading2.py Execution Starts: 18.940763132 Executions Ends: 33.942630292 Totals Execution Time:15.00 seconds.
🌐
Real Python
realpython.com › lessons › joining-threads
Joining Threads (Video) – Real Python
If you download the sample code, you can get your own copy of 05-join.py: ... To learn more, you can also check out the documentation. 00:00 In the previous lesson, we saw how we can add daemon=True to create a thread that will be killed once our main program completes. In this lesson, we’re going to learn about how we can have our main thread wait for other threads to finish before it completes, and this waiting for other threads is what we call join.
Published   November 19, 2019
🌐
Runebook.dev
runebook.dev › en › docs › python › library › threading › threading.Thread.join
python - The join() Method Explained: Common Threading Troubles and Solutions
It took longer than 4 seconds.") # The 'with' block automatically calls executor.shutdown(wait=True), # which is equivalent to joining all threads submitted to the pool. print("All managed tasks (or timeout) are complete.") ... The prompt specifically mentions "Operating System の Unix platforms (platform)" which likely refers to using Python's os module and possibly the platform module to deal with Unix-like systems (Linux
🌐
LinkedIn
linkedin.com › pulse › insight-threading-joining-threads-technique-python-ehsan-ahmadi
Insight into threading and joining Threads technique in Python
September 9, 2021 - For example, change the code like this: ... thread start thread finished process exited Executing — /Users/Ehsan/test_threading2.py Execution Starts: 18.940763132 Executions Ends: 33.942630292 Totals Execution Time:15.00 seconds.
🌐
Python3
python3.info › performance › threading › join.html
7.6. Threading Join — Python - from None to AI
TODO.task_done() sleep(1) print(f'Exiting {self.name}') # Create new threads def spawn_worker(count=1): for i in range(count): thread = MyThread() thread.start() RUNNING.append(thread) if __name__ == '__main__': spawn_worker(5) # Fill the queue with LOCK: for task in ['One', 'Two', 'Three', 'Four', 'Five']: TODO.put(task) # Wait for queue to empty while not TODO.empty(): pass # Notify threads it's time to exit EXIT = True # Wait for all threads to complete for thread in RUNNING: thread.join() print(f'Exiting Main Thread')
🌐
TestMu AI Community
community.testmuai.com › ask a question
What is the purpose of using Python thread join() in this scenario? - TestMu AI Community
December 2, 2024 - What is the use of join() in Python threading? I was studying Python threading and came across the join() method. The author mentioned that when a thread is in daemon mode, we need to use join() to ensure the thread completes before the main thread terminates.