Igor's Techno Club

Java's Unknown Features: CompletionService

In this series of articles, I will share with you the features that, for some reason, no one knows about or those that are rarely used.


Java's concurrency utilities, found in the java.util.concurrent package, provide robust tools for simplifying concurrent programming and enhancing reliability. If you are new to these concepts, you might find it helpful to start with the official Java concurrency tutorials.

The java.util.concurrent package (abbreviated as j.u.c) offers several key classes and interfaces to facilitate concurrent programming:

Practical Examples of Concurrent Programming

Let’s explore some practical examples of how you can utilize these tools in modern applications.

Example 1: Asynchronous Data Processing in Web Applications

Consider a web application that must process numerous user-generated data points simultaneously, such as a real-time analytics dashboard. Here's how you might handle this:

  1. Submit Callables to an ExecutorService: Each Callable processes a set of data and stores results. Since each data set is independent, they do not need to be processed in any specific order.
  2. CompletionService for Efficient Task Management: Use a CompletionService to manage these tasks. This service allows you to queue up future tasks and handle them as they complete, without polling.
ExecutorService executor = Executors.newFixedThreadPool(10);
CompletionService<DataResult> service = new ExecutorCompletionService<>(executor);

List<Callable<DataResult>> tasks = createDataProcessingTasks();
tasks.forEach(service::submit);

for (int i = 0; i < tasks.size(); i++) {
    try {
        Future<DataResult> completedFuture = service.take();
        DataResult result = completedFuture.get();
        updateDashboard(result);
    } catch (InterruptedException | ExecutionException e) {
        handleTaskError(e);
    }
}

This approach avoids inefficient polling and improves responsiveness by handling tasks as soon as they complete.

Example 2: Enhancing UI Responsiveness in Desktop Applications

In a desktop application, you might need to load multiple resources like images or files simultaneously to improve startup time. Here's how a CompletionService could be used:

  1. Parallel Resource Loading: Use a CompletionService to load each resource in a separate task, allowing them to complete independently and as quickly as possible.
  2. Immediate UI Updates: As each resource is loaded, the UI can be updated immediately to reflect the new content, enhancing the user's perception of responsiveness.
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletionService<Resource> service = new ExecutorCompletionService<>(executor);

List<Callable<Resource>> loadTasks = createResourceLoadTasks();
loadTasks.forEach(service::submit);

try {
    for (int i = 0; i < loadTasks.size(); i++) {
        Future<Resource> future = service.take();
        Resource resource = future.get();
        updateUIWithResource(resource);
    }
} catch (InterruptedException | ExecutionException e) {
    handleLoadingError(e);
}

This method significantly reduces the time needed to load multiple resources by utilizing parallel execution and immediate UI updates.

These examples demonstrate how java.util.concurrent can be leveraged to build more responsive and efficient applications by simplifying the management of concurrent tasks and handling asynchronous operations effectively. Whether for web or desktop applications, these tools are indispensable for modern Java developers.

#concurrency #java #juf