Java 20 ist erschienen

31.03.2023

Am 21.03.2023 ist Java 20 erschienen. Welche Neuerungen die Version mit sich bringt, erfahrt ihr hier.

JEP 429: Scoped Values (Incubator)

Zuvor gab es ThreadLocal, um Variablen in einem Thread zu definieren.

Allerdings gab es hier folgende Probleme:

  • Lese- und Schreibzugriff sind nicht getrennt
  • Beim Erben von ThreadLocal wird eine Kopie des ganzen Threads erzeugt, um Authentizität zu gewährleisten, was zu hohem Speicherbedarf führt (Variable in Thread A wird nicht von Variable in Thread B beeinflusst auch wenn B von A erbt)
  • Die Lebenszeit des Threads ist „unbound“, wodurch ein Memory Leak entsteht, wenn beim Ende nicht ThreadLocal::remove aufgerufen wird

Die Lösung für diese Probleme lautet ScopedValues und wie auch ThreadLocal nutzt ScopedValue die Virtual Thread API. Dabei werden beim Erben vom Thread keine Kopien erzeugt, was diese weitaus skalierbarer macht. Ebenso kann die Variable nun nur noch einmal gesetzt werden und ist nur in einem durch eine Lambda-Funktion definierten Scope nutzbar. Danach wird remove() automatisch aufgerufen.

JEP 432: Record Patterns (Second Preview)

Bereits in Java 19 hinzugekommen, sind die Record Patterns, welche es erlauben ohne Cast oder Aufruf von Gettern auf die Felder eines Records zuzugreifen. Diese können in for, switch oder instanceof verwendet werden.

public record Position(int x, int y) {}

Object object = new Position(42, 777);

if (object instanceof Position(int x, int y)) {
  System.out.printf("Type: position, x = %d, y = %d", x, y);
}

Hier wird direkt auf die Position x und y zugegriffen, und es werden 42 und 777 auf die Konsole geschrieben. Im Falle vom switch-case sieht es wie folgt aus:

Object object = new Position(123, 321)

switch (object) {
  case Position(int x, int y) -> System.out.printf("Type: position, x = %d, y = %d", x, y);
  // weitere cases
}

Mit Java 20 wurden nun in der zweiten Preview weitere Features eingebracht. Mitunter Record Patterns in for-Schleifen, sowie ein besserer Support vom Compiler im Umgang mit Record Patterns vom generischen Typen <T>.  Im Falle von for-Schleifen sieht die Änderung wie folgt aus:

List<Position> positions = ...

for (Position p : positions) {
  System.out.printf("x = %d, y = %d %n", p.getX(), p.getY());
}

// Mit dem Record Pattern
for (Position(int x, int y) : positions) {
   System.out.printf("x = %d, y = %d %n", x, y);
}

JEP 433: Pattern Matching for switch (Fourth Preview)

In Java 16 wurde mit JEP 394 Pattern Matching für instanceof eingeführt und in Java 17 mit JEP 406 Pattern Matching für switch.

static double area(Shape shape) {
    return switch (shape) {
        case Circle(int radius) -> radius * radius * Math.PI;
        case Rectangle(int width, int height) -> width * height;
    };
}

Wie man sieht, kann man das gut mit dem Record Pattern kombinieren. Hier erfolgt ein impliziter Cast zur Zielklasse, und es wird die entsprechende Berechnung für die Unterklasse vorgenommen. Falls keiner der case-Fälle matcht, fliegt eine MatchException. 

JEP 434: Foreign Function & Memory API (Second Preview)

Bereits seit Java 14 mit JEP 370 wird an dieser API entwickelt, um das langsame JNI abzulösen. Mit dieser API soll letztlich das Ausführen von Foreign Functions (Code außerhalb der JVM) leichter möglich sein. Dafür ist die Memory-API von großer Bedeutung, um off-heap Speicher für die foreign function zu allokieren. Mittels try-with-resources wird sichergestellt, dass der allokierte Speicherbereich automatisch wieder freigegeben wird.

JEP 436: Virtual Threads (Second Preview)

Virtuelle Threads werden von der JVM gehandhabt und sind so konzipiert, dass sie keine Betriebssystem-Threads blockieren, während sie auf externe Ressourcen warten müssen oder von andere Quellen blockiert werden. In erster Linie ist dieses Feature durch den Antrieb entstanden, Threads simpel umsetzen können zu wollen. Gleichzeitig möchte man auch keine unnütze Wartezeit generieren (busy-waiting), wofür eine reaktive Anwendung mittels CompletableFuture oder RxJava notwendig wäre, was allerdings relativ kompliziert umzusetzen ist. Virtual Threads kombinieren also das Beste aus diesen zwei Welten.

JEP 437: Structured Concurrency (Second Incubator)

Structured Concurrency befindet mit Java 20 ein weiteres mal in Inkubation. Das Feature ist aus der Problematik heraus entstanden, dass man Threads an jeder Stelle seines Codes starten kann, wobei es leicht ist, relativ schnell den Überblick zu verlieren. Zur Qualitätssicherung und besseren Lesbarkeit wurde also mit Java 19 eine neue API entwickelt.

public <T> T race(List<Callable<T>> tasks, Instant deadline)
        throws ExecutionException {
    try (var scope = new StructuredTaskScope.ShutdownOnSuccess<T>()) {
        // launch each task (implicitly in one virtual thread per task)
        for (var task : tasks)
            scope.fork(task);

        // wait for tasks to finish
        scope.joinUntil(deadline);

        // return the single result
        // (throws if no fork completed successfully)
        return scope.result();
    }
}

Mit try-with-resources wird hier ein klarer Scope definiert, innerhalb dessen die Nebenläufigkeit stattfindet. Die Strategie ist mit StructuredTaskScope.ShutdownOnSuccess so gewählt, dass beim ersten erfolgreichen V-Thread alle anderen Threads abgebrochen werden. Analog ist auch die Strategie StructuredTaskScope.ShutdownOnFailure verfügbar, welche beim ersten Fehlerfall abbricht und die Fehlermeldung hochreicht.

Sonstiges

  • Sicherheit
    • DTLS 1.0 als Standard deaktiviert
    • Bei Nutzung von (D)TLS erhält man nun über SSLParameters den Austauschalgorithmus und kann diesen auch setzen
    • JNDI mit LDAP oder RMI benötigen nun die Freischaltung der erlaubten Klassen via sog. „factoriesFilter“
  • Performance
    • Der G1 Garbage Collector wurde verbessert und als Standard deaktiviert, da er für manche Anwendungen Overhead bedeutet
    • JFR bekommt 2 neue Events bzgl. Security
      • InitialSecurityProperty bzgl. Java.security.Security Configuration
      • SecurityProviderService bzgl. Java.security.Provider.getService()
  • Compiler und jmod
    • Compiler Lint-Option „lossy-conversion“ warnt nun im Falle eines impliziten Casts. Bsp.: long a = 1L; a += 0.1 * 3L;
    • Jmod erhält nun die „–compression“ config zur ZIP-Kompression, wobei die Range von 0 (am schwächsten) bis 9 (am stärksten) geht (default: 6).
  • Regex-Verbesserungen im Umgang mit Gruppennamen. Matcher und Pattern erhalten ein Mapping von den Indizes der Gruppen zu deren Namen mittels namedGroups(), sowie weitere Funktionen im Umgang mit den Gruppen. Außerdem erhält Matcher die Methode hasMatch(), die überprüft, ob es einen Match gibt, ohne dass zuvor find() aufgerufen werden muss.
  • Neue HTTP Client keep-alive property (für v1.1 und v2.0 ist Standard nun 30s → zuvor 1200s)
  • Unicode 15 Support → 4489 neue Zeichen → insgesamt nun 149.186

Ausblick

Mit so vielen Features in Preview kann man nur schwer davon ausgehen, dass es das ein oder andere Feature in die nächste LTS mit Langzeitsupport schafft. Die Veröffentlichung von Java 21 LTS wird dann voraussichtlich im September 2023 stattfinden, also streicht euch diesen Zeitraum rot ein bis mehr bekannt ist. 

Quellen:

nipafx

openjdk

happycoders

 

Zurück zur Übersicht

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

*Pflichtfelder

*