A thread pool represents a group of worker threads which execute tasks. Each thread is reused to execute the next task when it becomes available.
Without a thread pool, we would be creating threads to execute tasks in an unbounded fashion. Having too many threads can incur great overhead.
In most real use-cases, we can use Java's standard ExecutorService to create a thread pool.
This is just an exercise to implement a thread pool to understand how it works.
