Question: Why this code doesn't work in parallel?

When the thread with odd number starts calculating its big number, other threads for some reason just wait for it to finish although they are supposed to do their own stuff. What am I missing?

```
import threading, math, time

class MyThread(threading.Thread): 

    def __init__(self, num):
        super(MyThread, self).__init__()
        self.num = num

    def run(self):
        while True:
            with mutex:
                print(self.num, 'started')
            math.factorial(self.num % 2 * 100000000)
            with mutex:
                print(self.num, 'finished')
            time.sleep(2)

mutex = threading.Lock()
threads = [MyThread(i) for i in range(5)]
for th in threads:
    th.start()

```

Answer: 
Python threads don't actually introduce true parallelism. Owing to the GIL (global interpreter lock) there can only be one interpreter thread per processor core. See [GlobalInterpreterLock](https://wiki.python.org/moin/GlobalInterpreterLock).

What's happening is the work is being divided up among your various threads, who then execute one at a time on the GIL. To quote realpython.com's [An Intro to Threading in Python](https://realpython.com/intro-to-python-threading/).

> 
> A thread is a separate flow of execution. This means that your program will have two things happening at once. But for most Python 3 implementations the different threads do not actually execute at the same time: they merely appear to.
> 
> 
> 

For true parallelism, you'd have to use the [`multiprocessing`](https://docs.python.org/3/library/multiprocessing.htm) library, which will:

> 
> effectively side-stepping the Global Interpreter Lock by using subprocesses instead of threads. Due to this, the multiprocessing module allows the programmer to fully leverage multiple processors on a given machine.
> 
> 
>
This behavior is related to cPyhton specific implementation detail (using the global interpreter lock as explained in the other answer)

However, When running this script with jython (Java implementation of Python) you get what you expected.

```
$ jython main.py
(0, 'started')
(2, 'started')
(3, 'started')
(1, 'started')
(4, 'started')
(0, 'finished')
(2, 'finished')
(4, 'finished')
...

```