Vaadin Executor
The Vaadin Executor provides a centralized mechanism for managing asynchronous tasks in Vaadin applications. It offers a configurable thread pool that can be used for executing background operations without blocking the UI thread.
This feature is particularly useful for performing time-consuming operations asynchronously, executing background tasks
that should not block the UI, managing concurrent operations efficiently and integrating with framework-specific task execution mechanisms,
like Spring TaskExecutor or CDI ManagedExecutor.
By default, Vaadin creates a thread pool executor with the following configuration:
-
Core pool size: 8 threads
-
Maximum pool size: Unbounded (Integer.MAX_VALUE)
-
Keep-alive time: 60 seconds for idle threads
-
Custom thread factory that creates daemon threads
-
Core threads are allowed to time out when idle
This default configuration is suitable for most applications but can be customized as needed.
Accessing and Using the Executor
You can access the executor through the VaadinService instance:
Source code
Java
Executor executor = VaadinService.getCurrent().getExecutor();
// Execute a task asynchronously
executor.execute(() -> {
// Your background task here
myService.longRunningTask();
});
// Execute a task asynchronously using CompletableFuture
CompletableFuture.supplyAsync(myService::longRunningTask, executor)
.thenAccept(this::handleResult);Code that runs in the executor doesn’t hold the session lock. To update the user interface from it, use signals or the UI.access() method, as described in Pushing UI Updates. For guidance on running background jobs and starting tasks from the user interface, see Background Jobs and User Interface Threads.
Configuring a Custom Executor
In standard Java applications, you can customize the executor by registering a VaadinServiceInitListener and providing your own executor implementation:
Source code
CustomExecutorServiceInitListener.java
CustomExecutorServiceInitListener.javapackage com.example;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import com.vaadin.flow.server.ServiceInitEvent;
import com.vaadin.flow.server.VaadinServiceInitListener;
public class CustomExecutorServiceInitListener implements VaadinServiceInitListener {
@Override
public void serviceInit(ServiceInitEvent event) {
ThreadPoolExecutor customExecutor = new ThreadPoolExecutor(
16, // Core pool size
32, // Maximum pool size
120, // Keep-alive time
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
r -> {
Thread thread = new Thread(r, "CustomVaadinExecutor-" + r.hashCode());
thread.setDaemon(true);
return thread;
});
// Allow core threads to time out
customExecutor.allowCoreThreadTimeOut(true);
// Set the custom executor
event.setExecutor(customExecutor);
}
}Register your listener using Java’s ServiceLoader mechanism by creating a file at
META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener.
The file should contain the fully qualified name of your implementation class:
com.example.CustomExecutorServiceInitListener
When configuring a custom Thread Pool, consider the following best practices:
-
Core Pool Size: Set based on the number of concurrent tasks your application typically handles. A good starting point is the number of CPU cores.
-
Maximum Pool Size: Set to a reasonable upper limit to prevent resource exhaustion. Consider your server’s memory and CPU constraints.
-
Queue Capacity: Use a bounded queue to prevent memory issues when task submission rate exceeds execution rate.
-
Use descriptive thread names to make debugging easier.
-
Make threads daemon threads (
Thread.setDaemon(true)) to prevent them from blocking JVM shutdown. -
Allow core threads to time out if your application has periods of inactivity
2EF7B593-E426-479E-89D7-E62667D316C2