Java 19 Officially released on September 20, 2022, Java 19 is not a long term support release until March 2023 when it will be replaced by JDK 20, this update brings a total of 7 new features.

1
2
3
4
5
The update brings seven new features.
➜ bin . /java -version
openjdk version "19" 2022-09-20
OpenJDK Runtime Environment (build 19+36-2238)
OpenJDK 64-Bit Server VM (build 19+36-2238, mixed mode, sharing)

OpenJDK Java 19 download: https://jdk.java.net/19/

OpenJDK Java 19 documentation: https://openjdk.java.net/projects/jdk/19/

7 new features brought by Java 19.

405 Record pattern matching (Preview)
425 Virtual Threads (Preview)
427 Switch pattern matching (three previews)
422 Linux/RISC-V Port
426 Vector API (Four incubations)
424 External Functions & Memory API (Preview)
428 Structured Concurrency (Incubator)

JEP 405: Record Pattern matching (preview)

record is a new type, it is essentially a final class, while all properties are final modified, it will automatically compile public get hashcode, equals, toString and other methods, reducing the amount of code written. record was proposed in Java 14 in Java 14, previewed in Java 15, and released in Java 16.

Example: Write a Dog record class, defining name and age properties.

1
2
3
4
package com.javaisland;

public record Dog(String name, Integer age) {
}

The use of Record.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.javaisland;

public class Java14Record {

    public static void main(String[] args) {
        Dog dog1 = new Dog("Sheepdog", 1);
        Dog dog2 = new Dog("Field Dog", 2);
        Dog dog3 = new Dog("Husky", 3);
        System.out.println(dog1);
        System.out.println(dog2);
        System.out.println(dog3);
    }
}

Output results.

1
2
3
Dog[name=sheepdog, age=1]
Dog[name=field dog, age=2]
Dog[name=Husky, age=3]

In Java 19, enhanced pattern matching was brought to Record to allow type conversion after using instanceof.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class RecordTest {
    public static void main(String[] args) {
        Object dog1 = new Dog("Sheepdog", 1);
        if(dog1 instanceof Dog dogTemp){
            System.out.println(dogTemp.name());
         }
    }
}
record Dog( String name, Integer age ){
}

// ➜ bin . /java RecordTest.java
// Sheepdog

You can even get a reference to a variable in Record directly when using instanceof.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class RecordTest2 {

    public static void main(String[] args) {
        Object dog1 = new Dog("Sheepdog", 1);
        if(dog1 instanceof Dog(String name,Integer age)){
            System.out.println(name+": "+age);

         }
    }
}

record Dog( String name, Integer age ){
}
//➜ bin . /java --enable-preview --source 19 RecordTest2.java
//Note: RecordTest2.java uses the preview feature of Java SE 19.
//Note: For more information, please recompile using -Xlint:preview.
//Shepherd:1

JEP 425: Virtual Threads (preview)

A very useful new feature, introduced gradually from Java 19, virtual threads are lightweight threads that can improve system throughput while significantly reducing the amount of code written and improving maintainability.

Thread has always been a very important part of Java concurrent programming. Thread is the concurrency unit in Java, and each Thread thread provides a stack to store local variables and method calls, as well as information about the thread context.

But the problem is that threads, like processes, are an expensive resource, and the JDK implements Threads as wrappers for OS threads, which means they are expensive and limited in number. That’s why we use thread pools to manage threads and limit the number of threads. For example, the commonly used Tomcat will use a separate thread for each request and limit the number of threads processing the request to prevent it from crashing due to too many threads; this is likely to run out of threads before the CPU or network connection is exhausted, thus limiting the throughput of the web service.

You might say that you can abandon the one-to-one approach of requests and threads and use asynchronous programming to solve this problem by segmenting the request processing and combining them into sequential pipelines that are managed through a set of APIs so that a limited number of threads can be used to handle more requests than the number of threads. This is certainly possible, but the attendant problems are

  • Additional learning of asynchronous programming is required.
  • Increased code complexity, which amounts to abandoning the language’s basic sequential combinatorial operations.
  • Stack context information becomes difficult to track.
  • Debug is difficult.
  • Conflicts with the programming style of the Java platform itself, where the Java concurrency unit is a Thread, and this is an asynchronous pipeline.

Virtual Threads

For all the above reasons, Java 19 introduced virtual threads, which are no different from Threads in terms of experience and are compatible with the previous API, but in comparison they take up very few resources and optimize the efficiency of hardware usage, so they are very easy to use and do not need to be pooled.

Here is an example, create 100,000 threads, then all sleep for 1 second and finally print the time consumed, if you open the traditional Thread thread way, resources are very tight; if the thread pool way, there must be some threads waiting for thread release; but using the virtual thread way, can be completed instantly.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class ThreadTest {

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            IntStream.range(0, 100_000).forEach(i -> {
                executor.submit(() -> {
                    Thread.sleep(1000);
                    return i;
                });
            });
        } // executor.close() will be called automatically
        // 100,000 virtual threads are submitted, and each thread sleeps for 1 second, finishing in about 1 second
        System.out.println("elapsed time:" + (System.currentTimeMillis() - start)+"ms");
    }
}

After execution, we found that the execution finished in 1.3 seconds, which is amazingly fast.

1
2
3
4
5
➜ bin . /java --enable-preview --source 19 ThreadTest.java
Note: ThreadTest.java uses the preview feature of Java SE 19.
Note: For more information, please recompile with -Xlint:preview.
Time:1309ms
➜ bin

Note: Virtual threads only increase the throughput of the program, they do not increase the processing speed of the program.

JEP 427: switch pattern matching (three previews)

Switch pattern matching was introduced in Java 17, previewed twice in Java 18, and now previewed three times in Java 19, with the same functionality as in Java 18 New Features - Switch, the improved Switch pattern matching can be more concise code and clearer logic, here are some usage examples to compare.

The following are a few examples.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Before JDK 17
static String formatter(Object o) {
    String formatted = "unknown";
    if (o instanceof Integer i) {
        formatted = String.format("int %d", i);
    } else if (o instanceof Long l) {
        formatted = String.format("long %d", l);
    } else if (o instanceof Double d) {
        formatted = String.format("double %f", d);
    } else if (o instanceof String s) {
        formatted = String.format("String %s", s);
    }
    return formatted;
}

And after Java 17, it can be improved by writing the following.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// After JDK 17
static String formatterPatternSwitch(Object o) {
    return switch (o) {
        case Integer i -> String.format("int %d", i);
        case Long l -> String.format("long %d", l);
        case Double d -> String.format("double %f", d);
        case String s -> String.format("String %s", s);
        default -> o.toString();
    };
}

switch can be combined with null to determine.

1
2
3
4
5
6
7
static void testFooBar(String s) {
    switch (s) {
        case null -> System.out.println("Oops");
        case "Foo", "Bar" -> System.out.println("Great");
        default -> System.out.println("Ok");
    }
}

Complex expressions can be added to case when

1
2
3
4
5
6
7
8
static void testTriangle(Shape s) {
    switch (s) {
        case Triangle t && (t.calculateArea() > 100) ->
            System.out.println("Large triangle");
        default ->
            System.out.println("A shape, possibly a small triangle");
    }
}

The type determination can be done when case

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
sealed interface S permits A, B, C {}
final class A implements S {}
final class B implements S {}
record C(int i) implements S {} // Implicitly final

static int testSealedExhaustive(S s) {
    return switch (s) {
        case A a -> 1;
        case B b -> 2;
        case C c -> 3;
    };
}

Extension: JEP 406: Type Matching for Switch preview

JEP 422: Linux/RISC-V Port

RISC-V is a free and open source RISC Instruction Set Architecture (ISA), in fact RISC-V is a series of related ISAs that are now supported by Java 19.

JEP 424: External Functions & Memory API (preview)

This feature introduces an API that allows Java developers to interact with code and data outside of the JVM by calling external functions (outside of the JVM) and safely accessing external memory (not managed by the JVM), allowing Java programs to call native libraries and work with native data without as many security risks as JNI.

This is not a new feature, it has been introduced since Java 14, and this time it has been optimized for performance, versatility, security, and ease of use.

History.

  • Java 14 JEP 370 introduced the external memory access API (incubator).
  • Java 15 JEP 383 introduced external memory access API (second incubator).
  • Java 16 JEP 389 introduces the external linker API (incubator).
  • Java 16 JEP 393 introduces the external memory access API (third incubator).
  • Java 17 JEP 412 introduces the external function and memory API (incubator).
  • Java 18 JEP 419 introduces external functions and memory API (secondary incubator).

Other updates

JEP 426: Vector API (Quadruple Incubator)

Achieves superior performance compared to equivalent scalar computation by reliably compiling at runtime to a vector computation representation of vector instructions on supported CPU architectures. This feature has been incubated for the fourth time and has been described in previous Java 16 ~ Java 18, so it is not repeated here.

JEP 428: Structured Concurrency (Incubation)

Simplifies error handling and cancellation, improves reliability and enhances observability by simplifying multi-threaded programming and treating multiple tasks running in different threads as a single unit of work.