I'd like to create a service that uses a pool of X threads and runs a single command every Y seconds, even if each thread takes minutes to complete. If the entire thread pool has been exhausted, then the service waits for the next thread to become available and executes it.
For example, if we have a simple method that outputs "Hello World" and sleeps/delays for 10 mintues, and X = 6 and Y = 10 then...
The system would output "Hello World" every 10 seconds for up to a minute. It would then pause for 9 more minutes, as no thread was available, and output "Hello World" every 10 seconds as threads become available, and so on.
I tried using ScheduledExecutorService, but the scheduleAtFixedRate waits for each thread to complete before starting the next one. Is there a variation or another ExecutorService class/method that would allow me to run multiple calls concurrently while still waiting if the thread pool is exhausted?
True, but there's no command in the ScheduledExecutorService API to automatically run threads concurrently. The methods scheduleAtFixedRate and scheduleWithFixedDelay only run a single thread at a time. I could manually re-create one of the methods with the schedule() command, but that I feel like I'm re-inventing the wheel.
One other solution would be to call scheduleAtFixedRate() multiple times with different delays, but ordering of threads goes out the door. For example, if I schedule one thread at 10 seconds, one at 20, one at 30 and so on.... then if the first thread is delayed but the other threads are available, the system will still wait 20 seconds to start the next task instead of using one of the other available threads.
I would just use scheduleAtFixedRate() to run a job that would, in turn, schedule a new job on the same executor, to do whatever it is you wanted. The first job's only purpose is to schedule the second, so it's vey unlikely to block unless you're just out of threads in the pool.
As for "ordering of threads goes out the door", well that's kind of a fundamental risk of wanting tasks to be able to execute concurrently, isn't it? We could probably insert some Latches or something to ensure that job B never starts unless A has already started, and C never starts unless B has started, etc. But as for when they finish, you're kind of on your own I think.
We could make the solution a little more robust by using two ScheduledExecutorServices - one just for the scheduling tasks, and one to do the actual work. The first probably needs only one thread, but the idea is to make sure it never can get blocked by the worker threads, except in the sense that if the JVM itself i has too much going on, anything can be blocked. We could also assign the scheduler thread higher priority to decrease its chance of blocking.