Allocating 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). Besides, if you unwittingly create too many threads, you can choke the CPU with it.
Changing from one thread to another is also a rather expensive process for the processor, each thread has its own context, and changing the context is a problem. So when you leave your Main Thread to create the new Thread, this can be a problem.
In a very simplified diagram, it works as follows:
The cost of switching these context switches is very high, and 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 look in a queue for any tasks to be performed (eg your Runnable).
Keeping 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 performed.
ThreadPools are often used to perform actions that happen routinely (for example, serving Web pages to a client. This happens every time a client accesses the page, it happens several times)
Summarizing:
Creating new threads is very expensive. By using a ThreadPool, you considerably reduce this cost, making your application more performative, as well as avoiding competition problems due to poor implementation.