import threading import time lock = threading.Lock() def high_priority(): while True: with lock: print("High priority thread acquired lock") time.sleep(0.2) def low_priority(): while True: with lock: print("Low priority thread acquired lock") # May never happen! time.sleep(0.1) t1 = threading.Thread(target=high_priority, daemon=True) t2 = threading.Thread(target=low_priority, daemon=True) t1.start() t2.start() time.sleep(1) I ran the code above 23 times before seeing low_priority get the lock. My aim is to understand what determines when within the 1 second sleep of main script low_priority thread gets the lock how often low_priority thread gets the lock However the experiments seem to require a long time so i'm looking for some theory first. Last 3 runs' output out of 23: ➜ deadlock python deadlock_priority.py High priority thread acquired lock High priority thread acquired lock High priority thread acquired lock High priority thread acquired lock High priority thread acquired lock ➜ deadlock python deadlock_priority.py High priority thread acquired lock High priority thread acquired lock High priority thread acquired lock High priority thread acquired lock High priority thread acquired lock ➜ deadlock python deadlock_priority.py High priority thread acquired lock High priority thread acquired lock High priority thread acquired lock Low priority thread acquired lock Low priority thread acquired lock Low priority thread acquired lock Low priority thread acquired lock Is priority determined by order of start method, or by length of sleep in the threads? t1.start() t2.start() I tried to sleep less on low_priority in hopes of giving it higher chance to take over, is this wishful thinking? I suspect time.sleep settings inside the lock context manager in both threads have no impact. Is there any way to make this experiment more reproducible? (meaning in a certain number of runs, i can see low_priority get the lock. What other parameters are interesting to tweak or add to the code to understand this or related issues better? I just wrote the code to study thread competition with no use case in mind, but intuition tells me there is some use. Can anyone explain if such patterns appear in meaningful code? I decided to randomly add sleeps outside the lock context manager and somehow low_priority gets the lock a lot more often? def high_priority(): while True: time.sleep(0.0001) with lock: print("High priority thread acquired lock") time.sleep(0.2) def low_priority(): while True: time.sleep(0.0001) with lock: print("Low priority thread acquired lock") time.sleep(0.1) Output of 1st 3 runs: ➜ deadlock python deadlock_sleep.py High priority thread acquired lock Low priority thread acquired lock High priority thread acquired lock Low priority thread acquired lock High priority thread acquired lock Low priority thread acquired lock High priority thread acquired lock ➜ deadlock python deadlock_sleep.py High priority thread acquired lock Low priority thread acquired lock High priority thread acquired lock Low priority thread acquired lock High priority thread acquired lock Low priority thread acquired lock High priority thread acquired lock ➜ deadlock python deadlock_sleep.py High priority thread acquired lock Low priority thread acquired lock High priority thread acquired lock Low priority thread acquired lock High priority thread acquired lock Low priority thread acquired lock High priority thread acquired lock Continue reading...