Last active
June 24, 2024 22:08
-
-
Save matteobertozzi/300ebade1143a2e1ddaec6e701049e40 to your computer and use it in GitHub Desktop.
Demo Virtual Threads - Synchronous but concurrent code, like async/await
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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