Skip to content

Instantly share code, notes, and snippets.

@matteobertozzi
Last active June 24, 2024 22:08
Show Gist options
  • Save matteobertozzi/300ebade1143a2e1ddaec6e701049e40 to your computer and use it in GitHub Desktop.
Save matteobertozzi/300ebade1143a2e1ddaec6e701049e40 to your computer and use it in GitHub Desktop.
Demo Virtual Threads - Synchronous but concurrent code, like async/await
// Run with JDK 21
// $ java -Djdk.virtualThreadScheduler.parallelism=1 -Djava.util.concurrent.ForkJoinPool.common.parallelism=1 DemoVirtualThreads.java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class DemoVirtualThreads {
private static final HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
public static ArrayList<String> demoHttp() throws Exception {
final ArrayList<String> output = new ArrayList<>();
// some cpu bound code
long result = 0;
for (int i = 0; i < 100; ++i) {
result += i * 3;
}
output.add(LocalDateTime.now() + ": step 1 - cpu bound code " + result);
// some blocking (I/O bound) code (4sec)
final HttpResponse<Void> resp = httpClient.send(HttpRequest.newBuilder()
.uri(URI.create("https://httpstat.us/200?sleep=4000"))
.GET()
.build(), BodyHandlers.discarding());
output.add(LocalDateTime.now() + ": step 2 - http result " + resp.statusCode());
// some othr blocking (I/O bound) code (2sec)
final HttpResponse<Void> resp2 = httpClient.send(HttpRequest.newBuilder()
.uri(URI.create("https://httpstat.us/200?sleep=2000"))
.GET()
.build(), BodyHandlers.discarding());
output.add(LocalDateTime.now() + ": step 3 - http result " + resp2.statusCode());
// more cpu bound code
long result2 = 0;
for (int i = 0; i < 3; ++i) {
Thread.sleep(250);
result2 += i * 3;
}
output.add(LocalDateTime.now() + ": step 4 - cpu bound code " + result2);
return output;
}
// To see the effect of virtual threads, and simulate async/await in other languages with a single thread,
// reduce the parallelism of forkjoin pool and the virtual thread scheduler.
//
// Adjust the common ForkJoinPool concurrency
// -Djava.util.concurrent.ForkJoinPool.common.parallelism=1
// Adjust the VirtualThread forkJoinPool concurrency
// -Djdk.virtualThreadScheduler.parallelism=1
// Show Carrier threads that are blocked (e.g. on a synchronized block)
// -Djdk.tracePinnedThreads=full
public static void main(final String[] args) throws Exception {
try (final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
// run 10 tasks doing slow blocking I/O
// each task is "slow" taking around 7sec to be executed
//
// if you are using a UI framework or a Network framework
// this code will be behind the scenes of your handles like onClick() or onRequestReceived()
final ArrayList<Future<ArrayList<String>>> futures = new ArrayList<>(10);
System.out.println(LocalDateTime.now() + ": Starting task submission");
for (int i = 0; i < 10; ++i) {
futures.add(executor.submit(DemoVirtualThreads::demoHttp));
}
System.out.println(LocalDateTime.now() + ": all task submitted, wait around 7sec...");
// wait each task to be complete.
// since they are all doing I/O operation they all finish around the same time
// even if there is just a single carrier thread executing them.
// so, they behave like a language with async/await.
for (int i = 0; i < futures.size(); ++i) {
final ArrayList<String> result = futures.get(i).get();
System.out.println(LocalDateTime.now() + ": Task " + i + " steps:");
for (final String r: result) {
System.out.println(" - " + r);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment