Async vs Threads
Handling concurrent work
Introduction
In this lesson, you'll learn about async vs threads in Java. Coming from JavaScript, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In JavaScript, you're familiar with handling concurrent work.
Java has its own approach to handling concurrent work, which we'll explore step by step.
The Java Way
Let's see how Java handles this concept. Here's a typical example:
import java.util.concurrent.*;
// CompletableFuture (Java 8+) — like JS Promise
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return fetchUser(1); // runs in thread pool
});
// Chain like .then()
future
.thenApply(user -> "Hello, " + user)
.thenAccept(System.out::println)
.exceptionally(err -> { System.out.println("Error: " + err); return null; });
// Parallel — like Promise.all
CompletableFuture<String> userF = CompletableFuture.supplyAsync(() -> fetchUser(1));
CompletableFuture<String> postF = CompletableFuture.supplyAsync(() -> fetchPosts(1));
CompletableFuture.allOf(userF, postF).join();Comparing to JavaScript
Here's how you might have written similar code in JavaScript:
// Async/await (single-threaded event loop)
async function fetchUser(id) {
const res = await fetch("/api/users/" + id);
return res.json();
}
// Parallel
const [user, posts] = await Promise.all([
fetchUser(1),
fetchPosts(1),
]);
// Error handling
try {
const data = await fetchUser(1);
console.log(data);
} catch (err) {
console.error("Failed:", err);
}You may be used to different syntax or behavior.
JS is single-threaded with an event loop; Java uses real threads
You may be used to different syntax or behavior.
JS async/await → Java CompletableFuture (Java 8+)
You may be used to different syntax or behavior.
JS Promise.all → CompletableFuture.allOf
You may be used to different syntax or behavior.
Java virtual threads (Java 21+) enable lightweight async-like code
Step-by-Step Breakdown
1. CompletableFuture vs Promise
CompletableFuture is Java's equivalent of a Promise — it represents a value that will be available in the future.
const result = await someAsyncOp();CompletableFuture<String> f = CompletableFuture.supplyAsync(() -> someOp());
String result = f.get(); // blocks until done2. Chaining
thenApply (transform result) and thenAccept (consume result) chain like JS .then().
fetch(url).then(r => r.json()).then(data => console.log(data))fetchAsync(url).thenApply(r -> parseJson(r)).thenAccept(System.out::println)3. Virtual Threads (Java 21+)
Java 21 virtual threads let you write blocking code that scales like async — a major simplification over CompletableFuture.
Thread.ofVirtual().start(() -> {
String data = fetchUser(1); // blocking but cheap
System.out.println(data);
});Common Mistakes
When coming from JavaScript, developers often make these mistakes:
- JS is single-threaded with an event loop; Java uses real threads
- JS async/await → Java CompletableFuture (Java 8+)
- JS Promise.all → CompletableFuture.allOf
Key Takeaways
- JS async/await → Java CompletableFuture
- Promise.all → CompletableFuture.allOf
- Java uses real threads; JS uses single-threaded event loop
- Java 21 virtual threads simplify concurrent code