There are several ways of running threads.
We can create an instance of Runnable and pass it to Thread class:
Or we can override the run() method in the Thread class directly:
However the better solution (in most cases) is using the Executor interface.
It gives us a couple of benefits. In previous cases, if we want to start again the thread that has come to the terminated state
we will get IllegalThreadStateException.
With Executor we can reuse the existing Runnable :
To prevent adding any more tasks to our executor we need to call shutdown() method.
What is even more important, it provides the ability to easily switch the implementation of running: using single thread, different kinds of thread pools or even scheduled periodic task.
We would just need to use different static factory method from Executors utility class:
This time our runnable is run using two threads from executor's thread pool.