New Features in Java 21
- Pattern Matching for Switch: Enhancements to the switch statement, allowing for pattern matching.
- Record Types: Introduction of record types for immutable data classes.
- Virtual Threads: Support for lightweight, user-mode threads to simplify concurrent programming.
What are Virtual Threads?
Virtual threads are a new lightweight implementation of threads in Java, aimed at simplifying concurrent programming. They allow developers to write code in a synchronous style while benefiting from the scalability of asynchronous programming.
With virtual threads, the Java platform aims to reduce the complexity of writing concurrent code, making it easier for developers to build high-performance applications.
Virtual threads are part of Project Loom, which introduces a new concurrency model to the Java platform.
Traditional threads in Java map directly to OS threads, which consume significant system resources. Virtual Threads are user-mode threads managed by the JVM rather than the operating system. Virtual Threads use far fewer resources compared to platform threads, enabling you to have millions of them.
Benefits of Virtual Threads
- Scalability: Virtual threads allow for a high level of concurrency, making it easier to scale applications.
- Simplicity: Developers can write code in a synchronous style, reducing the complexity of asynchronous programming.
- Resource Efficiency: Virtual threads consume fewer resources, allowing for more threads to be active at once.
How Do Virtual Threads Work?
Virtual threads work by allowing the JVM to manage thread scheduling and execution more efficiently. Instead of relying on the operating system to manage threads, the JVM can optimize the execution of virtual threads based on the application's needs.
This means that virtual threads can be created and destroyed more quickly than traditional threads, and the JVM can make better decisions about how to allocate resources to different threads.
Example Usage
Here's a simple example of using virtual threads in Java:
Runnable task = () -> {
System.out.println("Running in a virtual thread");
};
Thread.startVirtualThread(task);
Example: Using Virtual Threads in Java 21
In Java 21, using virtual threads is straightforward. Here's an example:
public class VirtualThreadsDemo {
public static void main(String[] args) throws InterruptedException {
// Creating a virtual thread
Thread virtualThread = Thread.ofVirtual().start(() -> {
System.out.println("Hello from virtual thread " + Thread.currentThread());
});
// Wait for the virtual thread to complete
virtualThread.join();
}
}
Performance Comparison: Virtual vs Platform Threads
Virtual threads are designed to be more efficient than traditional platform threads. They use less memory and can handle a larger number of concurrent tasks without the overhead associated with OS-level threads.
Let’s compare creating 10,000 threads with traditional platform threads and virtual threads.
public class ThreadComparison {
public static void main(String[] args) {
// Platform Threads
System.out.println("Platform Threads:");
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {}).start();
}
System.out.println("Completed in " + (System.currentTimeMillis() - startTime) + " ms");
// Virtual Threads
System.out.println("\nVirtual Threads:");
startTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
Thread.ofVirtual().start(() -> {});
}
System.out.println("Completed in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
Sealed classes
Sealed classes are a new feature in Java 21 that allow developers to control which classes can extend or implement them. This is useful for creating a more predictable and maintainable class hierarchy.
Benefits of Sealed Classes
- Controlled Extensibility: Sealed classes allow you to specify a limited set of subclasses, making it easier to reason about your code.
- Improved Maintainability: By restricting inheritance, you can reduce the risk of unintended side effects from changes in subclasses.
- Enhanced Pattern Matching: Sealed classes work well with pattern matching, allowing for more concise and readable code.
Why Use Sealed Classes?
- Control Hierarchies: Avoid unintended extensions/implementations.
- Enhanced Maintainability: Clear structure makes future changes safer and easier.
- Exhaustive Pattern Matching: Seamless integration with switch expressions.
Example of Sealed Classes
public sealed class Shape permits Circle, Rectangle {
}
public final class Circle extends Shape {
}
public final class Rectangle extends Shape {
}
Example: Using Sealed Classes
Define a Sealed Class:
// Define a Sealed Class:
public sealed class Shape permits Circle, Rectangle, Triangle {
public abstract double area(); // Abstract method for area calculation
}
// Circle is a permitted class extending Shape
public final class Circle extends Shape {
private final double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
// Rectangle is another permitted class
public final class Rectangle extends Shape {
private final double length, width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double area() {
return length * width;
}
}
// Triangle is also permitted
public non-sealed class Triangle extends Shape {
private final double base, height;
public Triangle(double base, double height) {
this.base = base;
this.height = height;
}
@Override
public double area() {
return 0.5 * base * height;
}
}