Given that each task is an infinite loop, I would use
newCachedThreadPool();
This will create a thread for each task that it needs (and no more)
The advantage of using a single pool with multiple pools is that you can disable the pool individually or give each thread a name, but if you don't need it, it's just an overhead.
Note. You can change the name of the stream using setName ("My task"), which may be useful for debugging / profiling purposes.
One way to use the ExecutorService is that it catches any uncaught exceptions / errors and places them in the returned Future object. Often this Future discarded, which means that if your task dies unexpectedly, it can also do it quietly.
I suggest you try / catch (Throwable) outside the loop and register it so you can see when the thread ever unexpectedly dies. e.g. OutOfMemoryError
source share