Skip to content
Dev Dump

Java Asynchronous Programming

b588d0f26efa3c8f969e62fb8faebc25_MD5

Asynchronous programming allows tasks to run in the background without stopping the main thread. This enables:

  • ๐Ÿงต Non-blocking execution
  • โšก Better resource use
  • ๐Ÿ’ป Smooth user interfaces
  • ๐Ÿ“ˆ High scalability for concurrent tasks
  • Goal โ†’ better responsiveness and parallelism
FeatureSynchronousAsynchronous
ExecutionBlocking, sequentialNon-blocking, concurrent
Thread UsageSingleMultiple
UI ResponsivenessMay blockRemains responsive
ComplexitySimpleRequires more control
Error Handlingtry-catchcallbacks, handlers
  • I/O operations (network, file, DB)
  • CPU-heavy tasks (math, ML)
  • UI apps (to avoid freezing)
  • Backend services (handle many requests)

๐Ÿ‘‰ Example (Real-life analogy):

  • Synchronous: Order coffee โ†’ wait until itโ€™s made โ†’ then do other work.

  • Asynchronous: Order coffee โ†’ work on laptop โ†’ barista notifies you when coffee is ready.

FeatureCallableRunnable
Returns valueโœ… Yes (V)โŒ No
Throws Exceptionโœ… Yes (checked exceptions allowed)โŒ No (only unchecked)
Genericsโœ… Supports genericsโŒ No
Introduced inJava 5Java 1.0
  • Callable<V> is like Runnable but:

    1. Can return a result (V)

    2. Can throw checked exceptions

  • Designed for asynchronous tasks that produce a result.

  • โœ” Returns result (V)

  • โ— Can throw checked exceptions

  • ๐Ÿง  Uses generics for type safety

  • Runnable cannot return results.

  • Callable can return a value + throw exceptions.

  • Future holds the result of asynchronous computation.

import java.util.concurrent.*;
public class CallableExample {
public static void main(String[] args) throws Exception {
Callable<String> task = () -> "Hello from Callable!";
FutureTask<String> future = new FutureTask<>(task);
new Thread(future).start();
String result = future.get(); // Waits for result
System.out.println(result);
}
}

Future represents the result of an async task, which will be available later.

  • get() โ€“ wait for result
  • cancel() โ€“ try to stop
  • isDone() โ€“ check completion
  • isCancelled() โ€“ check cancel
Future<Integer> f = executor.submit(() -> 42);
Integer result = f.get(); // Waits here
future.get(2, TimeUnit.SECONDS); // Throws TimeoutException if late
  • โŒ get() is blocking โ†’ doesnโ€™t feel truly async

  • โŒ No built-in chaining (cannot say do this after completion)

  • โŒ No easy error handling (exceptions wrapped in ExecutionException)

  • โŒ Cannot combine multiple futures (e.g., wait for both Task A & B)

๐Ÿ‘‰ Thatโ€™s why CompletableFuture (Java 8+) was introduced to overcome these issues.

a76f7a8258ec740e2c0e9db25ab23aa4_MD5

CompletableFuture improves on Future by adding:

  • โœ… Non-blocking chaining
  • ๐Ÿ”— Composability
  • ๐Ÿ’ฅ Error handling
  • ๐Ÿงช Manual completion
FeatureDescription
thenApplyTransforms result
thenAcceptConsumes result (no return)
exceptionallyHandle errors
thenCombineCombine multiple futures
allOf, anyOfWait for all/any futures
CompletableFuture.supplyAsync(() -> "Hi")
.thenApply(data -> data.toUpperCase())
.thenAccept(System.out::println);

Use thenCompose() to chain dependent tasks, and exceptionally() to handle errors.

thenCombine(f2, (a, b) -> a + b);
CompletableFuture.allOf(f1, f2).join();
CompletableFuture<String> future = new CompletableFuture<>();
future.complete("Done"); // Manually set result