The allocation of Threads is a somewhat costly issue.
Instantiating a new thread requires a call to the operating system and a memory allocation (each thread has its own stack). In addition to, if you don’t want to create too many threads, you can suffocate the CPU with this.
Switching from one thread to another is also a somewhat expensive process for the processor, each thread has its own context, and changing context is a problem. Therefore, when you exit your main Thread to create the new Thread, this may be a problem.
In a well simplified diagram, it works as follows:
The cost of exchanging all these context exchanges is very high, and it will happen every time you initialize a Thread.
When you use a Threadpool to perform your tasks, all Threads have already been allocated. Therefore, this cost has already been paid once. The allocated threads then search in a queue for any tasks to be performed (For example your Runnable).
Maintaining a Threadpool tends to be less costly than instantiating new Threads every time you perform an action.
However:
If the actions you perform are very sporadic (twice a day, for example), it may be more efficient to allocate a new Thread only when the action is executed.
Threadpools are mostly used to perform actions that happen routinely (for example, serving web pages to a client. This happens every time a customer accesses the page, so it happens several times)
In short:
Creating new Threads is very expensive. By using a Threadpool, you considerably reduce this cost, making your application more performative, while avoiding competition problems due to poor implementation
More Reading on Threads What is a Thread? How It Works?
– LeoColman