I think I realized what scares you, so here is my longer answer: terminology is a tiny bit misleading (obviously, or you wonβt ask this question, specifically putting emphasis on βreuseβ):
How do thread threads reuse threads?
What happens is that a single thread can be used to handle several tasks (usually passed as Runnable , but it depends on your "executor" structure: executors accept Runnable by default, but you can write your own "executor" / thread - pool accepts something more complex than Runnable [for example, a CancellableRunnable ]).
Now in the default implementation of ExecutorService , if a thread somehow terminates during use, it is automatically replaced by the new thread, but this is not the "reuse" they are talking about. In this case, "reuse" is absent.
So, the truth is, you cannot call start() twice in a Java thread, but you can pass as many Runnable as you want to the executor, and each Runnable run() method must be called once.
You can pass 30 Runnable up to 5 Java Thread , and each worker thread can call, for example, run() 6 times (practically does not guarantee that you will execute exactly 6 Runnable per Thread but this is a detail).
In this example, start() could be called 6 times. Each of these 6 start() will call exactly once the run() method for each Thread :
From Thread.start() Javadoc:
* Causes this thread to begin execution; the Java Virtual Machine * calls the <code>run</code> method of this thread.
BUT , then inside each run() thread, the Runnable method must be removed, and the run() method for each Runnable will be called. Therefore, each thread can handle several Runnable . This is what they call "stream reuse".
One way to make your own thread pool is to use the blocking queue that you run runnables on and have each of your threads after it has processed the run() Runnable method, delete the next Runnable (or block) and run its run() , then rinse and repeat.
I assume that part of the confusion (and this is a bit confusing) is because a Thread accepts Runnable and when calling start() the Runnable run() method is called, while the default thread pools also take Runnable .