Aus Linux-Magazin 06/2023

Neue Features in Java 20

© Roman Samborsky / 123RF.com

Ende März gab Oracle mit Java 20 ein letztes Feature-Release vor der LTS-Version im Herbst frei. Wir stellen Ihnen alle Neuerungen vor und beleuchten die aktuelle Entwicklung des Java-Ökosystems.

Ende März 2023 erschien mit Java 20 das letzte Feature-Release vor der für September 2023 geplanten Version 21 mit Langzeit-Support (LTS). Damit nehmen sich die Entwickler noch etwas Zeit, die offenen Features zu polieren, bevor sie mit der LTS-Fassung auch bei konservativen Entwicklern zum Einsatz kommen. Insgesamt arbeiteten sie in der neuen Version [1] an sieben Features, die sich alle noch im Incubator- oder Preview-Status befinden und damit noch nicht für die allgemeine Verwendung eignen.

Neben der Arbeit an den großen Features läuft die allgemeine Wartung und Pflege weiter [2]. Im JDK Bug System wurden seit Java 11 gut 21 000 Issues gefixt, gut zwei Drittel davon durch Oracle Mitarbeiter. Auf den Plätzen zwei und drei kommen Mitarbeiter von Red Hat und SAP. In Java 20 gehört zur Detailpflege unter anderem die Unterstützung des neuen Unicode-Standards mit 4489 neuen Zeichen.

Daneben gab es Aufräumarbeiten an bereits abgekündigten Funktionen, wie etwa allen Konstrukten in »Locale«. Ein Beispiel zeigt Listing 1: Statt des Codes aus Zeile 1 gilt es jetzt, die Varianten aus Zeile 2 oder 3 zu verwenden. Wer den »HttpClient« zur Abfrage sehr langsamer HTTP-Server nutzt, muss seinen Code ebenfalls ändern: Der Standard-Timeout wurde von extrem großzügigen 20 Minuten auf 30 Sekunden reduziert. Zeile 6 zeigt, wie man den Timeout separat setzen kann.

Listing 1

Locale und HttpClient

var l0 = new Locale("de", "DE"); // <-- deprecated
var l1 = Locale.of("de", "DE");
var l2 = new Locale.Builder().setLanguage("de").setRegion("DE").build();
final HttpClient httpClient = HttpClient.newBuilder()
  .version(HttpClient.Version.HTTP_2)
  .connectTimeout(Duration.ofSeconds(60)) // <-- Timeout setzen
  .build();

Rekordverdächtig

Mit Java 16 [3] wurden die Records als leichtgewichtige Alternative zu normalen Java-Klassen eingeführten. Dank der Record Patterns (JEP 432) und des Pattern Matching (JEP 433) lassen sie sich nun noch einfacher verwenden. Mit dem Operator »instanceof« kann man nicht nur auf den Record selbst zugreifen, sondern auch auf die darin enthaltenen Variablen.

Listing 2 zeigt den Zugriff auf den kompletten Record (Variable »p«, Zeile 4), auf einen enthaltenen Punkt (»m«, Zeile 7) und sogar auf geschachtelte Werte (»x« und »y«). Mit derselben Schreibweise kann man überdies in Foreach-Schleifen auf die Werte von Records zugreifen (Zeile 11). Passend dazu wurde im JEP 433 auch das Switch-Statement erweitert. Gerade in Kombination mit dem seit Java 19 verfügbaren Guards (»when«) erhält man kompakten, aber leserlichen Code (ab Zeile 15).

Listing 2

Record Patterns und Pattern Matching

record Punkt( double x, double y){};
record BoundingBox( Punkt min, Punkt max){};
// JEP 432: Record Pattern
if (obj instanceof Punkt p) {
  System.out.println("instanceof Punkt x/y: " + p.x + ", " + p.y);
}
else if (obj instanceof BoundingBox(var m, Punkt(var x, var y))) {
  System.out.println("instanceof BBox min. Punkt " + m);
  System.out.println("     max. " + x + ", " + y);
}
for (Punkt(double x, double y) : punkte) {
    System.out.printf("Punkt %f %f %n", x, y);
}
// JEP 433: Pattern Matching for switch
switch (obj) {
  case BoundingBox(var a, var b) when b.x > 5 -> System.out.println("switch a " + b);
  case BoundingBox bbox -> System.out.println("switch b " + bbox.max);
  default -> System.out.println("switch sonstiges");
}

Nebenläufig

Threads zur Entwicklung nebenläufiger Programme waren von Anfang an ein fundamentaler Bestandteil von Java. Bisher wurden sie in der JVM direkt auf einen Thread im darunterliegenden Betriebssystem abgebildet. Bei vielen wartenden Threads kostet das eine Menge Ressourcen, die man insbesondere im Cloud-Umfeld oft teuer einkaufen muss.

Mit Java 19 [4] wurden deshalb die im Projekt Loom entwickelten virtuellen Threads eingeführt, bei der die JVM selbst die virtuellen Threads verwaltet und zur Ausführung auf Threads des Betriebssystems abbildet. Die Idee ist keineswegs neu, ein ähnliches Konzept findet sich schon länger im Webserver Apache 2 (Apache-MPM Worker). Obwohl sich die virtuellen Threads noch im Preview-Status befinden, unterstützen viele Toolkits [5] sie bereits. Im Idealfall erfordert das Thema lediglich Konfigurationsänderungen im Webserver, jedoch keine (wesentlichen) Codeänderungen.

Die virtuellen Threads liegen mit JEP 436 nun im 2. Preview vor; gegenüber der Version in Java 19 ergeben sich nur minimale Änderungen. Ähnliches gilt für die Structured Concurrency (JEP 437). Damit lassen sich mehrere Threads zu Gruppen zusammenfassen, um parallele Berechnungen einfacher behandeln zu können.

Ganz neu hingegen sind die Scoped Values (JEP 429), die einen sauberen Zugriff auf Variablen aus Threads heraus ermöglichen. Viele Server starten für das Bearbeiten von Aufgaben einen eigenen Thread. Variablen zur Definition der Aufgabe selbst oder des Ausführungskontexts (wer hat wann diese Aufgabe gestellt?) müssen aufwendig durch alle beteiligten Schichten weitergegeben werden.

Als Alternative gibt es schon länger die Thread-lokalen Variablen, die nur im definierenden Thread und dessen Kindern zur Verfügung stehen. Dieses Konstrukt ist in der Verwendung nicht problemfrei. Beispielsweise können die Thread-lokalen Variablen jederzeit geändert werden, was leicht zu einem Memory-Leak führt. Bei aktuellen Servern mit unter 100 Threads lässt sich damit leben, mit den virtuellen Threads werden wir aber schnell mehrere Tausend Threads sehen.

Listing 3 zeigt die Verwendung der neuen Scoped Values. Die Hauptklasse enthält die Definition der Variablen »SCOPED_VALUE«, die damit erst einmal von allen Klassen aus verfügbar ist. Der eigentliche Trick findet sich in Zeile 8: Hier wird der Variablen ein Wert zugewiesen und anschließend ein Runnable gestartet. Innerhalb des Threads kann man jederzeit wie in Zeile 18 auf den Wert zugreifen. Es besteht also keine Notwendigkeit, den Namen des Anwenders über Konstruktoren oder Setter durch diverse Softwareschichten zu übertragen. Versucht man außerhalb des Threads auf die Variable zuzugreifen (Zeile 11), erhält man eine »NoSuchElementException« (Abbildung 1). Um das Aufräumen der Scoped Values muss man sich nicht separat kümmern; ein Setter zum Ändern ist gar nicht erst vorgesehen, um unangenehme Seiteneffekte zu vermeiden.

Abbildung 1: Die Ausgabe des Codes aus <a href="#artRef-l3">Listing&nbsp;3</a> mit Zugriffen auf Scoped Values.

Abbildung 1: Die Ausgabe des Codes aus Listing 3 mit Zugriffen auf Scoped Values.

Listing 3

Scoped Values

public class ServerKlasse {
  public final static ScopedValue<String> SCOPED_VALUE = ScopedValue.newInstance();
  public static void main(String... args) {
    System.out.println("-- Zugriff mit Scope --");
    String[] anwendende={"Lu-Tze", "Lobsang Ludd", "Susan Sto Helit"};
    for (var anwender : anwendende) {
      jdk.incubator.concurrent.ScopedValue.where(SCOPED_VALUE, anwender).run(new Aufgabe());
    }
    System.out.println("-- Zugriff ohne Scope --");
    new Thread(new Aufgabe()).start();
  }
  static class Aufgabe implements Runnable {
    @Override
    public void run() {
      try {
        // ... diverse Layer
        System.out.println("ScopedValue : " + ServerKlasse.SCOPED_VALUE.get());
      }
      catch (Exception exp) {
        System.err.println("Fehler beim Zugriff :" + exp);
      }
    }
  }
}

Fremde Welten

Die Foreign Function & Memory API (JEP 434) soll den Zugriff auf C-Funktionen einfacher und sicherer machen als mit Java Native Interface (JNI). Nach kleineren Änderungen liegt das Interface nun als zweite Preview vor, Verbesserungen finden nur noch in Details statt.

Gegenüber der bereits in Java 17 [6] vorgestellten Nutzung der rohen Basis-API hat sich einiges am Tooling geändert. Das Tool Jextract [7] erzeugt auf Basis der Header-Datei eine Java-Bibliothek, die die C-Bibliothek typsicher kapselt. Für Java 20 gibt es bereits eine fertig kompilierte Jextract-Version [8].

Ähnlich unauffällige Feinarbeit fand an der Vector-API (JEP 438) statt, mit der sich eines Tages die Vektoroperationen diverser CPUs direkt nutzen lassen sollen. Dieser Tag scheint aber noch in weiter Ferne zu liegen: Die Vector-API hat es inzwischen auf den fünften Inkubator gebracht.

Drumherum

Das Java-Universum umfasst weit mehr als nur die JVM. Gerade das umfangreiche Ökosystem aus Werkzeugen und Bibliothek macht die Sprache erst so attraktiv. Zu den Highlights gehört unter anderem das seit Java 11 von Gluon [9] entwickelte moderne UI-Framework JavaFX [10]. Die neueste Version JavaFX 20 enthält zahlreiche Security-Fixes und Detailverbesserungen. Sie können sie mit Java 17 und neuer einsetzen.

JavaFX selbst stellt vor allem eine solide Grundlage bereit und liefert Basis-Widgets für die Entwicklung von Oberflächen für den Desktop, das Web (Abbildung 2) [11] und mobile Geräte [12]. Die Entwicklung komplett neuer Widgets oder Skins geschieht in separaten Projekten wie Dirk Lemmermanns GemsFX (Abbildung 3).

Abbildung 2: Das JavaFX-basierete JFX-Central l&auml;uft im Webbrowser und als Desktop-Applikation.

Abbildung 2: Das JavaFX-basierete JFX-Central läuft im Webbrowser und als Desktop-Applikation.

Abbildung 3: GemsFX bietet eine Reihe zus&auml;tzlicher Widgets f&uuml;r JavaFX.

Abbildung 3: GemsFX bietet eine Reihe zusätzlicher Widgets für JavaFX.

Der Java-Server-Bereich ist aktuell stark in Bewegung. Der klassische Java Enterprise Server als Basis aller Kernprozesse einer Firma gilt inzwischen als total veraltet. Leichtgewichtigere Alternativen buhlen um die Aufmerksamkeit der Entwickler, die zwischen dem klassischen J2EE-Server, diversen Frameworks für Microservices oder No-Server-Anwendungen wählen können. Haupttreiber sind geringerer Speicherverbrauch und schnelleres Starten. Auf diese Weise läuft gerade in minutengenau abgerechneten Cloud-Infrastrukturen nur noch so viel Server-Kapazität, wie man gerade wirklich benötigt.

Das setzt voraus, dass die Anwendung überhaupt so gebaut ist, dass sie durch Starten weiterer Instanzen mehr Anwender tragen kann. Hier eine gesunde Balance zwischen Entwicklungsaufwand, Skalierbarkeit, Startzeit und Hauptspeicherverbrauch zu finden, erweist sich als Wissenschaft für sich. Egal, ob man auf die neueren Frameworks wie Quarkus [5], Micronaut [13], Helidon.io [14], Ktor [15] schaut oder die Weiterentwicklung bekannter Server wie Payara [16] oder Tomcat [17] betrachtet: Sie alle beanspruchen für sich die perfekte Entwicklerunterstützung bei minimalem Ressourcenverbrauch zu Laufzeit.

Ein anderes Mittel für die kosten- und energieeffiziente Umsetzung können die Native Images der GraalVM sein. Sie setzen Java-Code beim Kompilieren ähnlich wie C++-Code direkt in Maschinencode um und sollen so zur Laufzeit weniger Ressourcen benötigen. Das gibt es freilich nicht umsonst: Gewohnte Java-Features wie Plattformunabhängigkeit oder Optimierung zur Laufzeit bleiben hier naturgemäß außen vor. Wie weit das im Industriealltag trägt, lässt der Autor gerade einen Studenten untersuchen, die Ergebnisse lesen Sie in einer der nächsten Ausgaben.

Die GraalVM [18] wurde kontinuierlich weiterentwickelt, seit wir sie hier zuletzt vorgestellt haben [19]. Sowohl die Lizenz als auch der Sprachstandard entsprechen dem OpenJDK 19 und sind damit Business-tauglicher geworden. Auch das Tooling hat sich weiterentwickelt. Dazu gehören zum Beispiel die Unterstützung in Gradle, Github Actions oder Business-Features wie eine Software Bill of Materials und ein Vulnerability Scanner für die Native Images.

TornadoVM [20] wird an der Universität Manchester als experimentelle Alternative zum klassischen OpenJDK entwickelt. Hier lassen sich Teile der Berechnung auf GPUs oder FPGAs auslagern, die Programmierung erfolgt weiterhin in Java.

Fazit

Mit Java 20 wurde eine ganze Reihe von Features weiterentwickelt oder neu eingebracht. Alle befinden sich noch im Incubator- oder Preview-Status, die neue Version eignet sich deshalb vor allem zum Testen. Anders als in der Vorversion 19 gibt es keine umwälzenden Änderungen, für die der Wechsel bei produktiv eingesetzter Software lohnt. Es bleibt zu hoffen, dass bis zur für den Herbst vorgesehenen LTS-Version 21 möglichst viele Änderungen dem Sprung aus dem Incubator- oder Preview-Status schaffen. (jlu)

Der Autor

Carsten Zerbst erstellt mit seinem Team Software für Ingenieure, vom einfachen Konverter bis hin zur unternehmenskritischen Integrationslösung. Das Team sucht noch zwei Fachkräfte für abwechslungsreiche Tätigkeiten mit Java, C# oder C++ in Hamburg.

Infos

  1. OpenJDK 20: https://jdk.java.net/20
  2. “The Arrival of Java 20”: https://blogs.oracle.com/java/post/the-arrival-of-java-20
  3. Java 16: Carsten Zerbst, “Auf dem Sprung”, LM 06/2021, S. 76, https://www.lm-online.de/46129
  4. Java 19: Carsten Zerbst, “Auf dem Sprung”, LM 11/2022, S. 76, https://www.lm-online.de/48371
  5. Quarkus: https://quarkus.io
  6. Java 17: Carsten Zerbst, “Starker Auftritt”, LM 12/2021, S. 72, https://www.lm-online.de/46762
  7. Jextract: https://github.com/openjdk/jextract
  8. Jextract kompiliert: https://jdk.java.net/jextract
  9. Gluon: https://gluonhq.com
  10. JavaFX: https://openjfx.io
  11. JFX-Central: https://www.jfx-central.com
  12. JavaFX Mobile: https://gluonhq.com/products/mobile
  13. Micronaut: https://micronaut.io
  14. Helidon.io: https://helidon.io
  15. Ktor: https://ktor.io
  16. Payara: https://www.payara.fish
  17. Tomcat: https://tomcat.apache.org
  18. GraalVM: https://www.graalvm.org
  19. GraalVM: Carsten Zerbst, “Heiliger Graal?”, LM 07/2020, S. 82, https://www.lm-online.de/44518
  20. TornadoVM: https://www.tornadovm.org
DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 4 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben