Monitoring Async Operation Completion in Java
Java provides several mechanisms for executing asynchronous operations, with thread pools and concurrent utilities being the most frequently used approaches.
Using Future to Check Task Completion
The Future interface allows you to submit tasks to an ExecutorService and monitor their execution status. The isDone() method returns true once the task completes, either successfully or with an exception.
import java.util.concurrent.*;
public class TaskMonitor {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
Callable<String> work = () -> {
TimeUnit.MILLISECONDS.sleep(2000);
return "Operation finished!";
};
Future<String> pending = service.submit(work);
try {
while (!pending.isDone()) {
System.out.println("Still processing...");
Thread.sleep(500);
}
String outcome = pending.get();
System.out.println("Output: " + outcome);
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
} finally {
service.shutdown();
}
}
}
Parallel HTTP Requests in Web Applications
Asynchronous execution becomes valuable when coordinating multiple external calls. Submitting concurrent requests to different andpoints allows the application to wait for all responses without sequential blocking.
import java.util.concurrent.*;
import java.net.*;
import java.io.*;
public class ParallelHttpRequests {
public static void main(String[] args) throws Exception {
ExecutorService workers = Executors.newFixedThreadPool(4);
Callable<String> request1 = () -> retrieve("https://api.service.com/data");
Callable<String> request2 = () -> retrieve("https://api.service.com/status");
Future<String> response1 = workers.submit(request1);
Future<String> response2 = workers.submit(request2);
System.out.println("Response 1: " + response1.get());
System.out.println("Response 2: " + response2.get());
workers.shutdown();
}
private static String retrieve(String address) throws IOException {
HttpURLConnection connection = (HttpURLConnection) new URL(address).openConnection();
connection.setRequestMethod("GET");
BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream())
);
StringBuilder data = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
data.append(line);
}
reader.close();
return data.toString();
}
}
Exception Handling in Asynchronous Operations
Exceptions thrown within async tasks do not propagate synchronous. CompletableFuture provides exceptionally() to handle failures gracefully without crashing the application.
import java.util.concurrent.*;
public class AsyncExceptionHandling {
public static void main(String[] args) {
CompletableFuture<String> computation = CompletableFuture.supplyAsync(() -> {
throw new IllegalStateException("Process failure detected");
});
computation.thenAccept(result -> System.out.println("Value: " + result))
.exceptionally(error -> {
System.out.println("Error caught: " + error.getMessage());
return null;
});
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
Combining Results from Multiple Async Tasks
When coordinating dependent asynchronous operations, thenCombine() enables you to merge results from two concurrent tasks into a single computed value.
import java.util.concurrent.*;
public class MergingAsyncResults {
public static void main(String[] args) {
CompletableFuture<String> pipeline1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Phase one complete";
});
CompletableFuture<String> pipeline2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Phase two complete";
});
CompletableFuture<String> merged = pipeline1.thenCombine(pipeline2,
(first, second) -> first + " | " + second);
merged.thenAccept(finalResult -> System.out.println("Final: " + finalResult));
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}