Explore Callable interfaces and Future objects for asynchronous programming, enabling efficient non-blocking operations in your Java applications.
The Callable interface represents a task that returns a result and can throw an exception. This makes it more powerful than Runnable, which cannot return a value or throw checked exceptions.
Key differences between Callable and Runnable:
// Implementing Callable using lambda syntax
Callable<String> task = () -> {
// Some computation that returns a value
return "Task completed successfully";
};
// Submitting a Callable to an ExecutorService
ExecutorService executor = Executors.newCachedThreadPool();
Future<String> future = executor.submit(task);
A Future represents the result of an asynchronous computation. It provides methods to check if the computation is complete, wait for its completion, and retrieve the computation result.
When working with Future objects, remember:
// Retrieving results from a Future with exception handling
Future<String> future = executor.submit(task);
try {
// Block until the result is available
String result = future.get();
System.out.println("Task result: " + result);
} catch (InterruptedException e) {
// Handle thread interruption
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
// Handle exceptions thrown by the task
Throwable cause = e.getCause();
System.err.println("Task failed: " + cause.getMessage());
}
Waiting for a Future result can block your thread, but there are techniques to minimize blocking time.
Strategies include:
// Using a timeout to limit blocking time
Future<String> future = executor.submit(task);
try {
// Wait for at most 2 seconds
String result = future.get(2, TimeUnit.SECONDS);
System.out.println("Task result: " + result);
} catch (TimeoutException e) {
// Handle the timeout
System.out.println("Task took too long, cancelling");
future.cancel(true);
} catch (InterruptedException | ExecutionException e) {
// Handle other exceptions
}