Add java21-features module: runnable demos and captured output for the ankurm.com java21-features article
This commit is contained in:
@@ -103,3 +103,4 @@ Modules for the Java 8 / 11 / 17 / 21 feature articles, one folder each, each wi
|
||||
- `java8-features/` — see its README
|
||||
- `java11-features/` — see its README
|
||||
- `java17-features/` — see its README
|
||||
- `java21-features/` — see its README
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# java21-features — Java 21 features with examples
|
||||
|
||||
Companion code for the ankurm.com article **Java 21 Features with Examples: Virtual Threads, Record Patterns, Sequenced Collections**. The explanation lives in the article; this folder holds the runnable sources and the transcripts they produced. There is no `docs/` folder: the depth is inside the article itself.
|
||||
|
||||
```bash
|
||||
JDK21=/path/to/jdk21 ./run.sh # regenerates output/*.txt
|
||||
```
|
||||
|
||||
- `src/` compiles and runs unchanged on the JDK named in each output header.
|
||||
- `broken/` fails to compile on purpose; the compiler's own message is committed in `output/`.
|
||||
- `output/` is real captured output, regenerated by `run.sh`.
|
||||
|
||||
Tested on: OpenJDK 21.0.12.1 (the preview and incubator demos need --enable-preview / --add-modules, which run.sh passes).
|
||||
@@ -0,0 +1,5 @@
|
||||
/** Fails to compile on purpose: a more general pattern before a specific one makes the later case unreachable. */
|
||||
public class DominatedCase {
|
||||
static String f(Object o) { return switch (o) { case CharSequence cs -> "cs"; case String s -> "s"; default -> "d"; }; }
|
||||
public static void main(String[] args) { }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/** Fails to compile on purpose: a pattern switch over a sealed type must cover every permitted subtype. */
|
||||
public class NonExhaustive {
|
||||
sealed interface S permits A, B {}
|
||||
record A() implements S {}
|
||||
record B() implements S {}
|
||||
static String f(S s) { return switch (s) { case A a -> "a"; }; }
|
||||
public static void main(String[] args) { }
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/** Fails to compile on purpose: string templates are a preview feature in 21 and are off by default. */
|
||||
public class PreviewWithoutFlag {
|
||||
public static void main(String[] args) { String n = "x"; System.out.println(STR."hi \{n}"); }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
$ javac src/VirtualThreadsDemo.java && java VirtualThreadsDemo (21.0.12.1)
|
||||
tasks completed : 10000
|
||||
finished in < 5s: true (10,000 x 1s sleeps)
|
||||
isVirtual : true, daemon=true
|
||||
platform isVirtual: false
|
||||
@@ -0,0 +1,5 @@
|
||||
$ javac src/RecordPatternsDemo.java && java RecordPatternsDemo (21.0.12.1)
|
||||
line from (0,0) to (3,4) length^2=25
|
||||
box of String hi
|
||||
box of Integer 7
|
||||
no match
|
||||
@@ -0,0 +1,3 @@
|
||||
$ javac src/PatternSwitchDemo.java && java PatternSwitchDemo (21.0.12.1)
|
||||
big circle | circle | square 2.0 | rect 2.0x3.0
|
||||
null handled in the switch | large int | int | string of 4 | other
|
||||
@@ -0,0 +1,11 @@
|
||||
$ javac src/SequencedCollectionsDemo.java && java SequencedCollectionsDemo (21.0.12.1)
|
||||
getFirst/getLast: a c
|
||||
reversed view : [c, b, a]
|
||||
addFirst+removeLast: [z, a, b]
|
||||
LinkedHashSet reversed: [3, 2, 1]
|
||||
firstEntry/lastEntry : x=1 z=3
|
||||
putFirst : {w=0, x=1, y=2, z=3}
|
||||
reversed map : {z=3, y=2, x=1, w=0}
|
||||
view sees add : [3, 2, 1]
|
||||
immutable list : UnsupportedOperationException
|
||||
HashSet : no getFirst (unordered)
|
||||
@@ -0,0 +1,5 @@
|
||||
$ javac src/KemDemo.java && java KemDemo (21.0.12.1)
|
||||
algorithm : DHKEM
|
||||
secrets equal : true
|
||||
secret length : 32 bytes
|
||||
ciphertext length: 32 bytes
|
||||
@@ -0,0 +1,9 @@
|
||||
$ javac src/GcNamesDemo.java && java -XX:+UseZGC GcNamesDemo (21.0.12.1)
|
||||
gc bean : ZGC Cycles
|
||||
gc bean : ZGC Pauses
|
||||
|
||||
$ javac src/GcNamesDemo.java && java -XX:+UseZGC -XX:+ZGenerational GcNamesDemo (21.0.12.1)
|
||||
gc bean : ZGC Minor Cycles
|
||||
gc bean : ZGC Minor Pauses
|
||||
gc bean : ZGC Major Cycles
|
||||
gc bean : ZGC Major Pauses
|
||||
@@ -0,0 +1,6 @@
|
||||
$ javac --add-modules jdk.incubator.vector src/VectorDemo.java && java --add-modules jdk.incubator.vector VectorDemo (21.0.12.1)
|
||||
warning: using incubating module(s): jdk.incubator.vector
|
||||
1 warning
|
||||
WARNING: Using incubator modules: jdk.incubator.vector
|
||||
vector == scalar: true
|
||||
lanes >= 2 : true
|
||||
@@ -0,0 +1,2 @@
|
||||
$ javac --release 21 --enable-preview src/StringTemplatesPreview.java && java --enable-preview StringTemplatesPreview (21.0.12.1)
|
||||
Hello Ankur, 3 + 4 = 7
|
||||
@@ -0,0 +1,4 @@
|
||||
$ javac --release 21 --enable-preview src/UnnamedPreview.java && java --enable-preview UnnamedPreview (21.0.12.1)
|
||||
x only : 5
|
||||
loop count : 3
|
||||
caught, name not needed
|
||||
@@ -0,0 +1,2 @@
|
||||
$ java --enable-preview --source 21 src/UnnamedMainPreview.java (21.0.12.1)
|
||||
no class, no static, no String[] args
|
||||
@@ -0,0 +1,3 @@
|
||||
$ javac --release 21 --enable-preview src/ScopedValuesPreview.java && java --enable-preview ScopedValuesPreview (21.0.12.1)
|
||||
inside handler : user=ankur
|
||||
outside : bound=false
|
||||
@@ -0,0 +1,3 @@
|
||||
$ javac --release 21 --enable-preview src/StructuredConcurrencyPreview.java && java --enable-preview StructuredConcurrencyPreview (21.0.12.1)
|
||||
both subtasks : ankur / 42
|
||||
failure shuts down siblings: java.lang.IllegalStateException: boom
|
||||
@@ -0,0 +1,2 @@
|
||||
$ javac --release 21 --enable-preview src/FfmPreview.java && java --enable-preview --enable-native-access=ALL-UNNAMED FfmPreview (21.0.12.1)
|
||||
strlen("Hello Ankur") = 11
|
||||
@@ -0,0 +1,18 @@
|
||||
$ javac broken/PreviewWithoutFlag.java (21.0.12.1)
|
||||
broken/PreviewWithoutFlag.java:3: error: string templates are a preview feature and are disabled by default.
|
||||
public static void main(String[] args) { String n = "x"; System.out.println(STR."hi \{n}"); }
|
||||
^
|
||||
(use --enable-preview to enable string templates)
|
||||
1 error
|
||||
|
||||
$ javac broken/NonExhaustive.java (21.0.12.1)
|
||||
broken/NonExhaustive.java:6: error: the switch expression does not cover all possible input values
|
||||
static String f(S s) { return switch (s) { case A a -> "a"; }; }
|
||||
^
|
||||
1 error
|
||||
|
||||
$ javac broken/DominatedCase.java (21.0.12.1)
|
||||
broken/DominatedCase.java:3: error: this case label is dominated by a preceding case label
|
||||
static String f(Object o) { return switch (o) { case CharSequence cs -> "cs"; case String s -> "s"; default -> "d"; }; }
|
||||
^
|
||||
1 error
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
# Java 21 features: every demo compiled and run on a real JDK 21; regenerates output/*.txt
|
||||
# JDK21=/path/to/jdk21 ./run.sh
|
||||
set -uo pipefail
|
||||
unset JAVA_TOOL_OPTIONS
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
JDK21="${JDK21:-/opt/jdks/jdk21}"
|
||||
OUT="$HERE/output"; mkdir -p "$OUT"; B="$(mktemp -d)"; trap 'rm -rf "$B"' EXIT
|
||||
cd "$HERE"
|
||||
ver() { "$1/bin/java" -version 2>&1 | sed -n 1p | sed 's/.*"\(.*\)".*/\1/'; }
|
||||
# cr FILE CLASS [javac flags...] -- [java flags...]
|
||||
cr() { local src="$1" cls="$2"; shift 2; local jf=() rf=(); while [ $# -gt 0 ] && [ "$1" != "--" ]; do jf+=("$1"); shift; done; [ "${1:-}" = "--" ] && shift; rf=("$@")
|
||||
local o; o="$(mktemp -d -p "$B")"
|
||||
echo "\$ javac ${jf[*]:-} $src && java ${rf[*]:-} $cls ($(ver "$JDK21"))"
|
||||
"$JDK21/bin/javac" "${jf[@]}" -encoding UTF-8 -d "$o" "$src" 2>&1 | grep -v -e '^Note:' ; "$JDK21/bin/java" "${rf[@]}" -cp "$o" "$cls" 2>&1; }
|
||||
co() { local src="$1"; shift; local o; o="$(mktemp -d -p "$B")"
|
||||
echo "\$ javac $* $src ($(ver "$JDK21"))"; local r; r="$("$JDK21/bin/javac" "$@" -encoding UTF-8 -d "$o" "$src" 2>&1 | grep -v -e '^Note:')"; if [ -n "$r" ]; then echo "$r"; else echo "(compiled cleanly)"; fi; }
|
||||
PV=(--release 21 --enable-preview)
|
||||
cr src/VirtualThreadsDemo.java VirtualThreadsDemo > "$OUT/01-virtual-threads.txt"
|
||||
cr src/RecordPatternsDemo.java RecordPatternsDemo > "$OUT/02-record-patterns.txt"
|
||||
cr src/PatternSwitchDemo.java PatternSwitchDemo > "$OUT/03-pattern-switch.txt"
|
||||
cr src/SequencedCollectionsDemo.java SequencedCollectionsDemo > "$OUT/04-sequenced-collections.txt"
|
||||
cr src/KemDemo.java KemDemo > "$OUT/05-kem.txt"
|
||||
{ cr src/GcNamesDemo.java GcNamesDemo -- -XX:+UseZGC; echo; cr src/GcNamesDemo.java GcNamesDemo -- -XX:+UseZGC -XX:+ZGenerational; } > "$OUT/06-generational-zgc.txt"
|
||||
cr src/VectorDemo.java VectorDemo --add-modules jdk.incubator.vector -- --add-modules jdk.incubator.vector > "$OUT/07-vector-api.txt"
|
||||
cr src/StringTemplatesPreview.java StringTemplatesPreview "${PV[@]}" -- --enable-preview > "$OUT/08-string-templates.txt"
|
||||
cr src/UnnamedPreview.java UnnamedPreview "${PV[@]}" -- --enable-preview > "$OUT/09-unnamed-patterns.txt"
|
||||
{ echo "\$ java --enable-preview --source 21 src/UnnamedMainPreview.java ($(ver "$JDK21"))"; "$JDK21/bin/java" --enable-preview --source 21 src/UnnamedMainPreview.java 2>&1 | grep -v '^Note:'; } > "$OUT/10-unnamed-main.txt"
|
||||
cr src/ScopedValuesPreview.java ScopedValuesPreview "${PV[@]}" -- --enable-preview > "$OUT/11-scoped-values.txt"
|
||||
cr src/StructuredConcurrencyPreview.java StructuredConcurrencyPreview "${PV[@]}" -- --enable-preview > "$OUT/12-structured-concurrency.txt"
|
||||
cr src/FfmPreview.java FfmPreview "${PV[@]}" -- --enable-preview --enable-native-access=ALL-UNNAMED > "$OUT/13-ffm.txt"
|
||||
{ co broken/PreviewWithoutFlag.java; echo; co broken/NonExhaustive.java; echo; co broken/DominatedCase.java; } > "$OUT/14-compile-errors.txt"
|
||||
exit 0
|
||||
@@ -0,0 +1,16 @@
|
||||
import java.lang.foreign.*;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
/** JEP 442 (third preview in 21): call C's strlen without JNI. Needs --enable-preview. */
|
||||
public class FfmPreview {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
Linker linker = Linker.nativeLinker();
|
||||
MethodHandle strlen = linker.downcallHandle(linker.defaultLookup().find("strlen").orElseThrow(),
|
||||
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment s = arena.allocateUtf8String("Hello Ankur");
|
||||
long len = (long) strlen.invokeExact(s);
|
||||
System.out.println("strlen(\"Hello Ankur\") = " + len);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.management.*;
|
||||
|
||||
/** JEP 439 (final): Generational ZGC is opt-in on 21 with -XX:+ZGenerational. Prints the GC beans each mode registers. */
|
||||
public class GcNamesDemo {
|
||||
public static void main(String[] args) {
|
||||
for (GarbageCollectorMXBean b : ManagementFactory.getGarbageCollectorMXBeans()) System.out.println("gc bean : " + b.getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import javax.crypto.*;
|
||||
import java.security.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
/** JEP 452 (final): Key Encapsulation Mechanism API, with the DHKEM implementation that ships in 21. */
|
||||
public class KemDemo {
|
||||
public static void main(String[] args) throws Exception {
|
||||
KeyPairGenerator g = KeyPairGenerator.getInstance("X25519");
|
||||
KeyPair kp = g.generateKeyPair();
|
||||
KEM kem = KEM.getInstance("DHKEM");
|
||||
KEM.Encapsulator enc = kem.newEncapsulator(kp.getPublic());
|
||||
KEM.Encapsulated e = enc.encapsulate();
|
||||
SecretKey sender = e.key();
|
||||
SecretKey receiver = kem.newDecapsulator(kp.getPrivate()).decapsulate(e.encapsulation());
|
||||
System.out.println("algorithm : " + kem.getAlgorithm());
|
||||
System.out.println("secrets equal : " + Arrays.equals(sender.getEncoded(), receiver.getEncoded()));
|
||||
System.out.println("secret length : " + sender.getEncoded().length + " bytes");
|
||||
System.out.println("ciphertext length: " + e.encapsulation().length + " bytes");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/** JEP 441 (final): patterns in switch, guards with `when`, case null, and exhaustiveness over a sealed type. */
|
||||
public class PatternSwitchDemo {
|
||||
sealed interface Shape permits Circle, Square, Rect {}
|
||||
record Circle(double r) implements Shape {}
|
||||
record Square(double side) implements Shape {}
|
||||
record Rect(double w, double h) implements Shape {}
|
||||
|
||||
static String size(Shape s) {
|
||||
return switch (s) { // no default: the compiler checks all three cases
|
||||
case Circle c when c.r() > 10 -> "big circle";
|
||||
case Circle c -> "circle";
|
||||
case Square q -> "square " + q.side();
|
||||
case Rect(double w, double h) -> "rect " + w + "x" + h;
|
||||
};
|
||||
}
|
||||
static String any(Object o) {
|
||||
return switch (o) {
|
||||
case null -> "null handled in the switch";
|
||||
case Integer i when i > 100 -> "large int";
|
||||
case Integer i -> "int";
|
||||
case String str -> "string of " + str.length();
|
||||
default -> "other";
|
||||
};
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
System.out.println(size(new Circle(11)) + " | " + size(new Circle(1)) + " | " + size(new Square(2)) + " | " + size(new Rect(2, 3)));
|
||||
System.out.println(any(null) + " | " + any(500) + " | " + any(5) + " | " + any("abcd") + " | " + any(2.0));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/** JEP 440 (final): deconstruct records in instanceof and switch, nested. */
|
||||
public class RecordPatternsDemo {
|
||||
record Point(int x, int y) {}
|
||||
record Line(Point from, Point to) {}
|
||||
record Box<T>(T content) {}
|
||||
|
||||
static String describe(Object o) {
|
||||
if (o instanceof Line(Point(var x1, var y1), Point(var x2, var y2)))
|
||||
return "line from (" + x1 + "," + y1 + ") to (" + x2 + "," + y2 + ") length^2=" + ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
|
||||
if (o instanceof Box<?>(String s)) return "box of String " + s;
|
||||
if (o instanceof Box<?>(Integer i)) return "box of Integer " + i;
|
||||
return "no match";
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
System.out.println(describe(new Line(new Point(0, 0), new Point(3, 4))));
|
||||
System.out.println(describe(new Box<>("hi")));
|
||||
System.out.println(describe(new Box<>(7)));
|
||||
System.out.println(describe(new Box<>(2.5)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/** JEP 446 (preview in 21): ScopedValue binds a value for the dynamic extent of a call. Needs --enable-preview. */
|
||||
public class ScopedValuesPreview {
|
||||
static final ScopedValue<String> USER = ScopedValue.newInstance();
|
||||
static void handler() { System.out.println("inside handler : user=" + USER.get()); }
|
||||
public static void main(String[] args) {
|
||||
ScopedValue.where(USER, "ankur").run(ScopedValuesPreview::handler);
|
||||
System.out.println("outside : bound=" + USER.isBound());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import java.util.*;
|
||||
|
||||
/** JEP 431 (final): SequencedCollection, SequencedSet, SequencedMap. */
|
||||
public class SequencedCollectionsDemo {
|
||||
public static void main(String[] args) {
|
||||
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
|
||||
System.out.println("getFirst/getLast: " + list.getFirst() + " " + list.getLast());
|
||||
System.out.println("reversed view : " + list.reversed());
|
||||
list.addFirst("z"); list.removeLast();
|
||||
System.out.println("addFirst+removeLast: " + list);
|
||||
|
||||
LinkedHashSet<Integer> set = new LinkedHashSet<>(List.of(1, 2, 3));
|
||||
System.out.println("LinkedHashSet reversed: " + set.reversed());
|
||||
|
||||
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
|
||||
map.put("x", 1); map.put("y", 2); map.put("z", 3);
|
||||
System.out.println("firstEntry/lastEntry : " + map.firstEntry() + " " + map.lastEntry());
|
||||
map.putFirst("w", 0);
|
||||
System.out.println("putFirst : " + map);
|
||||
System.out.println("reversed map : " + map.reversed());
|
||||
|
||||
// reversed() is a view, not a copy
|
||||
List<String> src = new ArrayList<>(List.of("1", "2")); List<String> rev = src.reversed(); src.add("3");
|
||||
System.out.println("view sees add : " + rev);
|
||||
|
||||
try { List.of(1, 2).addFirst(0); } catch (UnsupportedOperationException e) { System.out.println("immutable list : UnsupportedOperationException"); }
|
||||
try { new HashSet<>(List.of(1)).getClass().getMethod("getFirst"); } catch (NoSuchMethodException e) { System.out.println("HashSet : no getFirst (unordered)"); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/** JEP 430 (preview in 21, withdrawn in later releases). Needs --enable-preview. */
|
||||
public class StringTemplatesPreview {
|
||||
public static void main(String[] args) {
|
||||
String name = "Ankur"; int a = 3, b = 4;
|
||||
System.out.println(STR."Hello \{name}, \{a} + \{b} = \{a + b}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import java.util.concurrent.StructuredTaskScope;
|
||||
|
||||
/** JEP 453 (preview in 21): fork subtasks, join, and let the scope own their lifetime. Needs --enable-preview. */
|
||||
public class StructuredConcurrencyPreview {
|
||||
public static void main(String[] args) throws Exception {
|
||||
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
|
||||
var user = scope.fork(() -> { Thread.sleep(50); return "ankur"; });
|
||||
var order = scope.fork(() -> { Thread.sleep(80); return 42; });
|
||||
scope.join().throwIfFailed();
|
||||
System.out.println("both subtasks : " + user.get() + " / " + order.get());
|
||||
}
|
||||
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
|
||||
scope.fork(() -> { throw new IllegalStateException("boom"); });
|
||||
scope.fork(() -> { Thread.sleep(5_000); return "never"; });
|
||||
scope.join().throwIfFailed();
|
||||
} catch (java.util.concurrent.ExecutionException e) {
|
||||
System.out.println("failure shuts down siblings: " + e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/** JEP 445 (preview in 21): an unnamed class with an instance main. Needs --enable-preview --source 21. */
|
||||
void main() {
|
||||
System.out.println("no class, no static, no String[] args");
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/** JEP 443 (preview in 21): unnamed patterns and variables, `_`. Needs --enable-preview. */
|
||||
public class UnnamedPreview {
|
||||
record Point(int x, int y) {}
|
||||
public static void main(String[] args) {
|
||||
Object o = new Point(5, 9);
|
||||
if (o instanceof Point(var x, _)) System.out.println("x only : " + x);
|
||||
int total = 0;
|
||||
for (var _ : java.util.List.of("a", "b", "c")) total++;
|
||||
System.out.println("loop count : " + total);
|
||||
try { Integer.parseInt("zz"); } catch (NumberFormatException _) { System.out.println("caught, name not needed"); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import jdk.incubator.vector.*;
|
||||
|
||||
/** JEP 448 (sixth incubator): needs --add-modules jdk.incubator.vector. Result must equal the scalar loop. */
|
||||
public class VectorDemo {
|
||||
static final VectorSpecies<Integer> S = IntVector.SPECIES_PREFERRED;
|
||||
public static void main(String[] args) {
|
||||
int n = 1000; int[] a = new int[n], b = new int[n], c = new int[n], d = new int[n];
|
||||
for (int i = 0; i < n; i++) { a[i] = i; b[i] = 2 * i; }
|
||||
int i = 0;
|
||||
for (; i < S.loopBound(n); i += S.length()) IntVector.fromArray(S, a, i).add(IntVector.fromArray(S, b, i)).intoArray(c, i);
|
||||
for (; i < n; i++) c[i] = a[i] + b[i];
|
||||
for (int k = 0; k < n; k++) d[k] = a[k] + b[k];
|
||||
System.out.println("vector == scalar: " + java.util.Arrays.equals(c, d));
|
||||
System.out.println("lanes >= 2 : " + (S.length() >= 2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/** JEP 444 (final): 10,000 blocking tasks on virtual threads. Timing is machine dependent, so only counts are asserted. */
|
||||
public class VirtualThreadsDemo {
|
||||
public static void main(String[] args) throws Exception {
|
||||
AtomicInteger done = new AtomicInteger();
|
||||
long t0 = System.nanoTime();
|
||||
try (ExecutorService ex = Executors.newVirtualThreadPerTaskExecutor()) {
|
||||
for (int i = 0; i < 10_000; i++) ex.submit(() -> { Thread.sleep(Duration.ofSeconds(1)); done.incrementAndGet(); return null; });
|
||||
}
|
||||
long ms = (System.nanoTime() - t0) / 1_000_000;
|
||||
System.out.println("tasks completed : " + done.get());
|
||||
System.out.println("finished in < 5s: " + (ms < 5_000) + " (10,000 x 1s sleeps)");
|
||||
|
||||
Thread v = Thread.ofVirtual().name("demo-vt").start(() -> System.out.println("isVirtual : " + Thread.currentThread().isVirtual() + ", daemon=" + Thread.currentThread().isDaemon()));
|
||||
v.join();
|
||||
Thread p = Thread.ofPlatform().name("demo-pt").start(() -> System.out.println("platform isVirtual: " + Thread.currentThread().isVirtual()));
|
||||
p.join();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user