Skip to content
Dev Dump

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.

☎️ Callable Interface (Why not Runnable?)

Section titled “☎️ Callable Interface (Why not Runnable?)”
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.

☎️ Callable Interface (Why not Runnable)

Section titled “☎️ Callable Interface (Why not Runnable)”
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