Currently, TimeScheduler3 has a runner thread which continually takes tasks from a DelayQueue and submits them to the thread pool.
If a task is non-blocking, this is not necessary; instead the task can be run on the runner's thread. This will reduce the number of thread created in the thread pool.
Investigate whether it is possible to mark tasks as blocking or non-blocking. Example: an AgeOutCache task is always non-blocking. A UNICAST3 or NAKACK2 retransmission task is (potentially) blocking if TCP is used as transport and non-blocking if for example UDP or TCP_NIO2 are used.