I'm trying to implement a Java program that prepares a bunch of runnables for databases(there are hundreds of them, and I want to allocate a thread for each database), and run each of those runnables using a ScheduledExecutorService, with an initial delay and some delay in between executions. This is what I've managed so far:
public static void main(String[] args) throws Exception {
// Get db name and info as key-value pairs
Map<String, DbInfo> dbInfoMap = getDbInfoMap();;
// Initialize the scheduled executor service.
final ScheduledExecutorService executorService = Executors
.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
// For each database, get a list of runnable and process accordingly.
dbInfoMap.forEach((db, dbInfo) -> {
// Get a list of runnable(s).
List<Runnable> runnableList = new MigrationTaskManager(db, dbInfo).getRunnableTasksForDB();
// Schedule all the runnable(s) using the scheduled executor service, and a delay
runnableList.forEach(runnable -> executorService
.scheduleWithFixedDelay(runnable, INITIAL_DELAY, DELAY, TimeUnit.MILLISECONDS));
});
}
}
This sort of does what I intend, albeit with some caveats (It takes a really long time to start processing some databases if the list is too long). How can I implement it so that all the runnables for each database get fired at the same time? For instance, If I have 100 databases to read from, how can I ensure that 100 threads get fired with a bunch of runnables in each thread when the program starts? I'd really appreciate some help with this.
tl;dr
For maximum efficiency, pass a collection of your hundreds of tasks to an executor service assigning one virtual thread per task.
Scheduling of a thread’s execution is out of your control
Your title says:
If you mean that you want all of the
Runnabletasks to execute simultaneously, that is not possible using Java threads.Platform threads
In Java, you ask for a thread to be executed, but when execution begins, and how long execution lasts, is up to the whims of the host OS’ thread scheduler. Threads in today’s Java are each mapped to a host OS thread. (Virtual threads in upcoming Java 21+ will be quite different than these “platform” threads.) A Java programmer has no control over when a thread runs.
You can collect a bunch of tasks to be submitted together to an executor service. Pass the collection of tasks
ExecutorService#invokeAll. ButinvokeAlldoes not mean they will be executed together. They can be executed in any order, with each task’s execution beginning at any time, and completing at any time. TheinvokeAllmethod is merely a convenience for you the programmer if you have chosen to instantiate all your tasks up front. You could just as well write a loop that submits each instantiated task object to the executor service, one task at a time.Here is an example app to demonstrate passing a collection of
Callableobjects to theinvokeAllmethod of anExecutorService.When run.
Virtual threads
In Java 21+, we have virtual threads, an alternative to platform threads. The scheduling of virtual threads is managed within the JVM rather than by the host OS. When the JVM detects a virtual thread is blocking, the virtual thread is dismounted from the “real” platform thread where it executes. The virtual thread is parked while waiting for the blocking to resolve. In the meantime, the JVM mounts another virtual thread onto the platform thread. This means vastly greater utilization of your CPU cores as the host OS threads rarely sit idle.
Database calls always involve much blocking, taking a long time while waiting for a response. You are making hundreds of such calls at a time. So this is ideal situation for switching to virtual threads rather than platform threads.
Switching to virtual threads is utterly simple: just swap out the
Executorscall.And we can increase our example calls to a thousand, or even a million. Virtual threads are vastly lightweight than platform threads. Millions of concurrent virtual threads is reasonable.
And let's not dump the entire
databaseInfoslist of a thousand. Just first and last, now easier to do asListimplementsSequencedCollectionwith convenientgetFirst&getLastmethods. See JEP 431: Sequenced Collections.When run: