Java 18~20 新特性
Java18新特性(2022年3月)
正式特性
默认使用UTF-8字符编码
从jdk18开始,默认使用UTF-8字符编码。
// 文件操作
new FileReader("file.txt") // 现在默认 UTF-8
new FileWriter("file.txt") // 现在默认 UTF-8
new PrintWriter("file.txt") // 现在默认 UTF-8
// 流操作
new InputStreamReader(inputStream) // 现在默认 UTF-8
new OutputStreamWriter(outputStream) // 现在默认 UTF-8
// 格式化输出
new PrintStream("file.txt") // 现在默认 UTF-8
new Formatter("file.txt") // 现在默认 UTF-8我们可以通过如下参数修改其他字符编码:
-Dfile.encoding=UTF-8字符集统一的好处
public class CharsetExample {
public static void main(String[] args) throws IOException {
String chinese = "你好,世界!";
// Java 18 之前,这在不同系统上可能有不同结果
try (FileWriter writer = new FileWriter("test.txt")) {
writer.write(chinese);
}
try (FileReader reader = new FileReader("test.txt")) {
char[] buffer = new char[1024];
int length = reader.read(buffer);
String result = new String(buffer, 0, length);
System.out.println("读取结果: " + result);
}
// 现在在所有平台上都会正确处理中文
System.out.println("默认字符集: " + Charset.defaultCharset());
// 输出: UTF-8
}
}简单的web服务器
可以通过jwebserver命令启动jdk18中提供的静态web服务器,可以利用该工具查看一些原型,做简单的测试。在命令提示符中输入jwebserver命令后会启动,然后在浏览器中输入:http://127.0.0.1:8000/ 即可看到当前命令提示符路径下的文件了。
# 启动简单的文件服务器,服务当前目录
jwebserver
# 指定端口和目录
jwebserver -p 8080 -d /path/to/your/files
# 绑定到特定地址
jwebserver -b 127.0.0.1 -p 9000实际使用场景:
# 快速分享文件
cd ~/Documents
jwebserver -p 8000
# 访问 http://localhost:8000 查看文件
# 测试静态网站
cd my-website/dist
jwebserver -p 3000
# 开发时的临时服务器
jwebserver -b 0.0.0.0 -p 8080 -d ./public这个工具主要用于
- 快速原型开发:不需要配置复杂的服务器.
- 文件分享:在局域网内快速分享文件
- 静态网站测试:测试前端项目
- 教学演示:简单的 HTTP 服务器示例
@snippet注解
Java 18 引入了 @snippet 标签,可以让 JavaDoc 生成的代码示例更美观,而且支持从外部文件引入代码片段。
/**
* 计算两个数的最大公约数
*
* {@snippet :
* int a = 48;
* int b = 18;
* int gcd = MathUtils.gcd(a, b);
* System.out.println("GCD: " + gcd); // @highlight substring="GCD"
* }
*
* @param a 第一个数
* @param b 第二个数
* @return 最大公约数
*/
public static int gcd(int a, int b) {
// 实现代码...
}
/**
* 从外部文件引入代码片段
*
* {@snippet file="examples/QuickSort.java" region="main-algorithm"}
*
* @param arr 要排序的数组
*/
public static void quickSort(int[] arr) {
// 实现代码...
}@snippet 的高级功能
/**
* 演示数组操作
*
* {@snippet :
* int[] numbers = {3, 1, 4, 1, 5}; // @highlight
* Arrays.sort(numbers); // @highlight
* System.out.println(Arrays.toString(numbers)); // @highlight substring="toString"
* // 输出: [1, 1, 3, 4, 5] // @replace regex="输出:" replacement="结果:"
* }
*/
public void demonstrateArraySort() {
// 方法实现
}
/**
* 使用外部代码文件
*
* {@snippet file="examples/DatabaseExample.java" region="connection-setup"}
*
* 上面的代码展示了如何建立数据库连接。
*/
public void setupDatabase() {
// 方法实现
}外部文件示例((examples/DatabaseExample.java)
public class DatabaseExample {
public void example() {
// @start region="connection-setup"
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "user";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, username, password)) {
// 使用连接进行数据库操作
} catch (SQLException e) {
e.printStackTrace();
}
// @end
}
}用方法句柄重新实现核心反射
Java 18 在内部使用方法句柄重新实现了核心反射功能,这个改进对开发者是透明的,但可以提供:
- 更好的性能:方法句柄比传统反射更快
- 更少的内存占用:减少了元数据开销.
- 更好的 JIT 优化:编译器可以更好地优化
互联网地址解析 SPI
Java 18 引入了互联网地址解析的服务提供者接口(SPI),允许自定义 DNS 解析:
// 自定义地址解析器
public class CustomInetAddressResolver extends InetAddressResolver {
@Override
public Stream<InetAddress> lookupByName(String host, LookupPolicy lookupPolicy) {
// 自定义 DNS 解析逻辑
if ("myapp.local".equals(host)) {
return Stream.of(InetAddress.getByName("127.0.0.1"));
}
// 委托给默认解析器
return InetAddressResolver.platformResolver().lookupByName(host, lookupPolicy);
}
@Override
public String lookupByAddress(byte[] addr) {
// 自定义反向 DNS 解析
return InetAddressResolver.platformResolver().lookupByAddress(addr);
}
}这个特性主要用于
- 企业内部 DNS:解析内部域名
- 负载均衡:自定义负载均衡策略
- 缓存优化:实现 DNS 缓存策略
- 测试环境:模拟不同的网络环境
将被移除的方法
在jdk18中标记了Object中的finalize方法,Thread中的stop方法将在未来被移除。
JIT 编译器优化
Java 18 包含了多项 川T 编译器优化
- 更好的循环优化:改进了循环展开和向量化
- 方法内联优化:更智能的内联决策
- 逃逸分析改进:减少不必要的堆分配
垃圾收集器改进
G1 垃圾收集器优化
# G1 在 Java 18 中的性能改进
java -XX:+UseG1GC \
-XX:MaxGCPauseMillis=100 \
-XX:G1HeapRegionSize=16m \
MyAppZGC 和 Shenandoah 优化
# ZGC 改进
java -XX:+UseZGC \
-XX:ZCollectionInterval=0 \
MyApp
# Shenandoah 改进
java -XX:+UseShenandoahGC \
-XX:ShenandoahGCMode=iu \
MyApp预览特性
switch 的模式匹配(第二次预览)
Java 18 继续完善 switch 的模式匹配
// 需要启用预览功能
// javac --enable-preview --release 18 PatternMatchingExample.java
// java --enable-preview PatternMatchingExample
public String processValue(Object obj) {
return switch (obj) {
case String s -> "字符串: " + s;
case Integer i -> "整数: " + i;
case Long l -> "长整数: " + l;
case Double d -> "浮点数: " + d;
case null -> "空值";
default -> "其他类型: " + obj.getClass().getSimpleName();
};
}
// 结合条件判断(守卫模式)
public String processString(Object obj) {
return switch (obj) {
case String s && s.length() > 10 -> "长字符串: " + s.substring(0, 10) + "...";
case String s && s.isEmpty() -> "空字符串";
case String s -> "字符串: " + s;
case null -> "空值";
default -> "非字符串";
};
}与 Records 和 Sealed 类结合
public sealed interface Shape permits Circle, Rectangle, Triangle {}
public record Circle(double radius) implements Shape {}
public record Rectangle(double width, double height) implements Shape {}
public record Triangle(double base, double height) implements Shape {}
public double calculateArea(Shape shape) {
return switch (shape) {
case Circle(var radius) -> Math.PI * radius * radius;
case Rectangle(var width, var height) -> width * height;
case Triangle(var base, var height) -> 0.5 * base * height;
// 不需要 default,sealed 类保证完整性
};
}
// 更复杂的模式匹配
public String describeShape(Shape shape) {
return switch (shape) {
case Circle(var r) && r > 10 -> "大圆,半径:" + r;
case Circle(var r) -> "小圆,半径:" + r;
case Rectangle(var w, var h) && w == h -> "正方形,边长:" + w;
case Rectangle(var w, var h) -> "矩形,宽:" + w + ",高:" + h;
case Triangle(var b, var h) -> "三角形,底:" + b + ",高:" + h;
};
}孵化器特性
向量 API(第三次孵化器)
Java 18 继续改进向量 API
import jdk.incubator.vector.*;
public class VectorOperations {
private static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
// 向量化的数组求和
public static float vectorSum(float[] array) {
float sum = 0.0f;
int upperBound = SPECIES.loopBound(array.length);
// 向量化部分
FloatVector accVector = FloatVector.zero(SPECIES);
for (int i = 0; i < upperBound; i += SPECIES.length()) {
FloatVector vector = FloatVector.fromArray(SPECIES, array, i);
accVector = accVector.add(vector);
}
sum += accVector.reduceLanes(VectorOperators.ADD);
// 处理剩余元素
for (int i = upperBound; i < array.length; i++) {
sum += array[i];
}
return sum;
}
// 向量化的数组乘法
public static void vectorMultiply(float[] a, float[] b, float[] result) {
int upperBound = SPECIES.loopBound(a.length);
for (int i = 0; i < upperBound; i += SPECIES.length()) {
FloatVector va = FloatVector.fromArray(SPECIES, a, i);
FloatVector vb = FloatVector.fromArray(SPECIES, b, i);
FloatVector vr = va.mul(vb);
vr.intoArray(result, i);
}
// 处理剩余元素
for (int i = upperBound; i < a.length; i++) {
result[i] = a[i] * b[i];
}
}
public static void main(String[] args) {
float[] numbers = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};
System.out.println("向量化求和: " + vectorSum(numbers));
float[] a = {1, 2, 3, 4, 5, 6, 7, 8};
float[] b = {8, 7, 6, 5, 4, 3, 2, 1};
float[] result = new float[8];
vectorMultiply(a, b, result);
System.out.println("向量化乘法结果: " + Arrays.toString(result));
}
}外部函数和内存 API(第二次孵化器)
Java 18 继续完善外部函数和内存 API
import jdk.incubator.foreign.*;
public class ForeignFunctionExample {
public static void main(String[] args) throws Throwable {
// 获取系统链接器
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
// 查找 C 标准库函数 strlen
MemoryAddress strlenAddr = stdlib.lookup("strlen").orElseThrow();
// 创建函数描述符
FunctionDescriptor strlenDesc = FunctionDescriptor.of(
CLinker.C_LONG, // 返回类型
CLinker.C_POINTER // 参数类型
);
// 创建方法句柄
MethodHandle strlen = linker.downcallHandle(strlenAddr, strlenDesc);
// 分配内存并调用函数
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment cString = CLinker.toCString("Hello, Foreign Function!", scope);
long length = (long) strlen.invoke(cString);
System.out.println("字符串长度: " + length);
}
// 调用 printf 函数
MemoryAddress printfAddr = stdlib.lookup("printf").orElseThrow();
FunctionDescriptor printfDesc = FunctionDescriptor.of(
CLinker.C_INT,
CLinker.C_POINTER
);
MethodHandle printf = linker.downcallHandle(printfAddr, printfDesc);
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment formatStr = CLinker.toCString("Hello from Java %d!\n", scope);
printf.invoke(formatStr, 2024);
}
}
}Java19新特性(2022年9月)
正式特性
Linux/RISC-V 端口
Java 19 正式支持 RISC-V 架构的 Linux 系统
# 在 RISC-V 系统上运行 Java
java -version
# 输出会显示 riscv64 架构信息
# RISC-V 是一个开源的指令集架构
# 主要用于嵌入式设备和服务器RISC-V 的特点
- 开源免费:不需要授权费用
- 可扩展性:支持自定义指令
- 生态发展:越来越多厂商支持
Thread.builder接口
在jdk19中新增了一个密封(sealed)接口Builder,该接口只允许有两个子接口实现:
OfPlatform:创建平台线程的时候使用,是一个密封接口,只允许ThreadBuilders.PlatformThreadBuilder实现。
OfVirtual:创建虚拟线程的时候使用,是一个密封接口,只允许ThreadBuilders.VirtualThreadBuilder实现。
上面3种创建虚拟线程的方式本质都是通过OfVirtual来进行创建的,OfVirtual和OfPlatform接口中的api很多是相同的,OfPlatform中的方法更多,所以下面我们以OfPlatform为例演示他的使用方式。
通过OfPlatform中的factory()方法可以创建一个ThreadFactory线程工厂,学过线程池的同学对它应该并不陌生,它可以帮助我们创建出平台线程对象。
ThreadFactory threadFactory = Thread.ofPlatform().factory();除了上面的用法之外,还可以用它来创建平台线程对象
//创建任务
Runnable task = () -> {
System.out.println(Thread.currentThread().getName());
};
//将任务放到t线程中并运行
Thread t = Thread.ofPlatform().start(task);上面创建平台线程的方式跟之前的new Thread是一样的,优点是我们可以用它来实现链式编程,比如要设置线程优先级,线程名字,守护线程:
//创建任务
Runnable task = () -> {
System.out.println(Thread.currentThread().getName());
};
//链式编程
Thread.ofPlatform().name("小").priority(Thread.MAX_PRIORITY).daemon(true).start(task);java.io包下类的变化
为了减少内存的使用,BufferedOutputStream,BufferedWriter,OutputStreamWriter中默认的初始数组大小由之前的8192变成了512。
预览特性
虚拟线程(预览)
Java 19 引入了虚拟线程作为预览特性,这是 Project Loom 的核心成果:
// 需要启用预览功能
// javac --enable-preview --release 19 VirtualThreadExample.java
// java --enable-preview VirtualThreadExample
public class VirtualThreadExample {
public static void main(String[] args) throws InterruptedException {
// 创建虚拟线程
Thread vThread = Thread.ofVirtual().start(() -> {
System.out.println("虚拟线程运行中: " + Thread.currentThread());
});
vThread.join();
// 使用虚拟线程执行器
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 创建大量虚拟线程
for (int i = 0; i < 100000; i++) {
final int taskId = i;
executor.submit(() -> {
try {
Thread.sleep(1000); // 模拟 I/O 操作
System.out.println("任务 " + taskId + " 完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
} // 自动关闭执行器
}
}- 虚拟线程中的ThreadLocal
由于虚拟线程的数量会比较多,所以在使用ThreadLocal的时候一定要注意。线程池中的线程在执行多个任务的时候,不要使用ThreadLocal。在Thread.Builder中提供了不支持ThreadLocal的方法。
Thread.ofVirtual().allowSetThreadLocals(false);
Thread.ofVirtual().inheritInheritableThreadLocals(false);- LockSupport对虚拟线程的支持
LockSupport是支持虚拟线程的,当调用park()方法时,虚拟线程会解除挂载,这样平台线程可以执行其他的操作,当调用unpark()方法时,虚拟线程会被调度器重新挂载到平台线程,再继续工作。
详情可以看这篇文章:虚拟线程
Record 模式(预览)
Java 19 引入了 Record 模式匹配
// 定义 Records
public record Point(int x, int y) {}
public record Person(String name, int age) {}
public class RecordPatternExample {
public static void main(String[] args) {
Object obj = new Point(3, 4);
// Record 模式匹配
if (obj instanceof Point(var x, var y)) {
System.out.println("点的坐标: (" + x + ", " + y + ")");
System.out.println("距离原点: " + Math.sqrt(x * x + y * y));
}
// 嵌套的 Record 模式
record Address(String city, String street) {}
record Employee(Person person, Address address) {}
Employee emp = new Employee(
new Person("张三", 30),
new Address("北京", "中关村大街")
);
// 嵌套解构
if (emp instanceof Employee(Person(var name, var age), Address(var city, var street))) {
System.out.println(name + "," + age + "岁,住在" + city + street);
}
}
}switch 的模式匹配(第三次预览)
Java 19 继续完善 switch 模式匹配
public String processValue(Object value) {
return switch (value) {
case String s -> "字符串: " + s;
case Integer i -> "整数: " + i;
case Point(var x, var y) -> "点: (" + x + ", " + y + ")";
case null -> "空值";
default -> "其他类型";
};
}
// 结合条件判断
public String analyzePoint(Object obj) {
return switch (obj) {
case Point(var x, var y) when x == 0 && y == 0 -> "原点";
case Point(var x, var y) when x == 0 -> "Y轴上的点";
case Point(var x, var y) when y == 0 -> "X轴上的点";
case Point(var x, var y) -> "普通点: (" + x + ", " + y + ")";
case null -> "空值";
default -> "不是点";
};
}外部函数和内存 API(预览)
Java 19 将外部函数和内存 API升级为预览特性
import java.lang.foreign.*;
public class ForeignAPIExample {
public static void main(String[] args) throws Throwable {
// 获取 C 标准库链接器
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
// 查找 strlen 函数
MemoryAddress strlen = stdlib.find("strlen").orElseThrow();
// 创建函数描述符
FunctionDescriptor descriptor = FunctionDescriptor.of(
ValueLayout.JAVA_LONG, // 返回类型
ValueLayout.ADDRESS // 参数类型
);
// 创建方法句柄
MethodHandle strlenHandle = linker.downcallHandle(strlen, descriptor);
// 分配内存并调用
try (Arena arena = Arena.ofConfined()) {
MemorySegment cString = arena.allocateUtf8String("Hello Foreign API!");
long length = (long) strlenHandle.invoke(cString);
System.out.println("字符串长度: " + length);
}
}
}孵化器特性
向量 API(第四次孵化器)
Java 19 继续改进向量 API
import jdk.incubator.vector.*;
public class VectorExample {
private static final VectorSpecies<Integer> SPECIES = IntVector.SPECIES_PREFERRED;
public static void vectorizedSum(int[] a, int[] b, int[] result) {
int upperBound = SPECIES.loopBound(a.length);
// 向量化循环
for (int i = 0; i < upperBound; i += SPECIES.length()) {
IntVector va = IntVector.fromArray(SPECIES, a, i);
IntVector vb = IntVector.fromArray(SPECIES, b, i);
IntVector vr = va.add(vb);
vr.intoArray(result, i);
}
// 处理剩余元素
for (int i = upperBound; i < a.length; i++) {
result[i] = a[i] + b[i];
}
}
public static void main(String[] args) {
int[] a = {1, 2, 3, 4, 5, 6, 7, 8};
int[] b = {8, 7, 6, 5, 4, 3, 2, 1};
int[] result = new int[8];
vectorizedSum(a, b, result);
System.out.println(Arrays.toString(result));
// [9, 9, 9, 9, 9, 9, 9, 9]
}
}结构化并发(孵化器)
Java 19 引入了结构化并发 API
import jdk.incubator.concurrent.StructuredTaskScope;
public class StructuredConcurrencyExample {
public static void main(String[] args) {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 并发执行多个任务
var task1 = scope.fork(() -> fetchUserData(1));
var task2 = scope.fork(() -> fetchUserPreferences(1));
var task3 = scope.fork(() -> fetchUserHistory(1));
// 等待所有任务完成或任一失败
scope.join();
scope.throwIfFailed();
// 获取结果
UserData userData = task1.resultNow();
UserPreferences prefs = task2.resultNow();
UserHistory history = task3.resultNow();
System.out.println("用户数据加载完成");
} catch (Exception e) {
System.err.println("加载用户数据失败: " + e.getMessage());
}
}
private static UserData fetchUserData(int userId) {
// 模拟网络请求
try { Thread.sleep(100); } catch (InterruptedException e) {}
return new UserData("张三", "zhangsan@example.com");
}
private static UserPreferences fetchUserPreferences(int userId) {
try { Thread.sleep(150); } catch (InterruptedException e) {}
return new UserPreferences("dark", "zh-CN");
}
private static UserHistory fetchUserHistory(int userId) {
try { Thread.sleep(200); } catch (InterruptedException e) {}
return new UserHistory(List.of("登录", "查看文档", "退出"));
}
}
record UserData(String name, String email) {}
record UserPreferences(String theme, String language) {}
record UserHistory(List<String> actions) {}Java20新特性(2023年3月)
Java 20 主要是继续完善预览特性,没有引入新的正式特性。
预览特性
Record 模式(第二次预览)
Java 20 继续完善 Record 模式
public class RecordPatternExample {
// 定义复杂的 Record 层次
public sealed interface Shape permits Circle, Rectangle {}
public record Circle(double radius) implements Shape {}
public record Rectangle(double width, double height) implements Shape {}
public record ColoredShape(Shape shape, String color) {}
public static void main(String[] args) {
ColoredShape redCircle = new ColoredShape(new Circle(5.0), "红色");
// 嵌套 Record 模式匹配
switch (redCircle) {
case ColoredShape(Circle(var radius), var color) ->
System.out.println(color + "圆形,半径:" + radius);
case ColoredShape(Rectangle(var width, var height), var color) ->
System.out.println(color + "矩形," + width + "x" + height);
}
// 在 if 语句中使用
if (redCircle instanceof ColoredShape(Circle(var r), var c) && r > 3) {
System.out.println("大" + c + "圆形");
}
}
}switch 的模式匹配(第四次预览)
Java 20 进一步改进了 switch 模式匹配
public class SwitchPatternExample {
public static String analyzeValue(Object value) {
return switch (value) {
case null -> "空值";
case String s when s.isEmpty() -> "空字符串";
case String s when s.length() == 1 -> "单字符: " + s;
case String s -> "字符串: " + s + " (长度: " + s.length() + ")";
case Integer i when i == 0 -> "零";
case Integer i when i > 0 -> "正整数: " + i;
case Integer i -> "负整数: " + i;
case Point(var x, var y) when x == y -> "对角线上的点: (" + x + ", " + y + ")";
case Point(var x, var y) -> "点: (" + x + ", " + y + ")";
default -> "未知类型: " + value.getClass().getSimpleName();
};
}
// 处理集合
public static String analyzeList(Object obj) {
return switch (obj) {
case null -> "空引用";
case List<?> list when list.isEmpty() -> "空列表";
case List<?> list when list.size() == 1 -> "单元素列表: " + list.get(0);
case List<?> list -> "列表,大小: " + list.size();
default -> "不是列表";
};
}
}外部函数和内存 API(第二次预览)
Java 20 继续完善外部函数和内存 API
import java.lang.foreign.*;
public class AdvancedForeignExample {
public static void main(String[] args) throws Throwable {
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
// 调用 qsort 函数进行排序
MemoryAddress qsort = stdlib.find("qsort").orElseThrow();
FunctionDescriptor qsortDesc = FunctionDescriptor.ofVoid(
ValueLayout.ADDRESS, // 数组指针
ValueLayout.JAVA_LONG, // 元素数量
ValueLayout.JAVA_LONG, // 元素大小
ValueLayout.ADDRESS // 比较函数指针
);
MethodHandle qsortHandle = linker.downcallHandle(qsort, qsortDesc);
// 创建比较函数
FunctionDescriptor compareDesc = FunctionDescriptor.of(
ValueLayout.JAVA_INT,
ValueLayout.ADDRESS,
ValueLayout.ADDRESS
);
MethodHandle compareFunction = MethodHandles.lookup()
.findStatic(AdvancedForeignExample.class, "compareInts",
MethodType.methodType(int.class, MemorySegment.class, MemorySegment.class));
MemorySegment compareStub = linker.upcallStub(compareFunction, compareDesc, Arena.global());
// 创建要排序的数组
try (Arena arena = Arena.ofConfined()) {
int[] javaArray = {64, 34, 25, 12, 22, 11, 90};
MemorySegment nativeArray = arena.allocateArray(ValueLayout.JAVA_INT, javaArray);
// 调用 qsort
qsortHandle.invoke(nativeArray, (long)javaArray.length,
ValueLayout.JAVA_INT.byteSize(), compareStub);
// 读取排序后的结果
int[] sorted = nativeArray.toArray(ValueLayout.JAVA_INT);
System.out.println("排序结果: " + Arrays.toString(sorted));
}
}
public static int compareInts(MemorySegment a, MemorySegment b) {
int valueA = a.get(ValueLayout.JAVA_INT, 0);
int valueB = b.get(ValueLayout.JAVA_INT, 0);
return Integer.compare(valueA, valueB);
}
}虚拟线程(第二次预览)
Java 20 改进了虚拟线程
public class VirtualThreadExample {
public static void main(String[] args) throws InterruptedException {
// 使用 Thread.Builder 创建虚拟线程
Thread.Builder builder = Thread.ofVirtual().name("worker-", 0);
List<Thread> threads = new ArrayList<>();
// 创建大量虚拟线程
for (int i = 0; i < 1000000; i++) {
final int taskId = i;
Thread thread = builder.start(() -> {
try {
// 模拟 I/O 密集型任务
Thread.sleep(Duration.ofMillis(100));
if (taskId % 100000 == 0) {
System.out.println("任务 " + taskId + " 完成,线程: " + Thread.currentThread());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
if (i < 10) { // 只保留前10个线程的引用用于演示
threads.add(thread);
}
}
// 等待部分线程完成
for (Thread thread : threads) {
thread.join();
}
System.out.println("虚拟线程演示完成");
}
}孵化器特性
作用域值(孵化器)
Java 20 引入了作用域值 API
import jdk.incubator.concurrent.ScopedValue;
public class ScopedValueExample {
// 定义作用域值
private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
private static final ScopedValue<String> REQUEST_ID = ScopedValue.newInstance();
public static void main(String[] args) {
// 绑定作用域值
ScopedValue.where(USER_ID, "user123")
.where(REQUEST_ID, "req456")
.run(() -> {
processRequest();
});
}
private static void processRequest() {
System.out.println("处理请求,用户ID: " + USER_ID.get());
System.out.println("请求ID: " + REQUEST_ID.get());
// 调用其他方法,作用域值自动传递
callService();
}
private static void callService() {
// 在任何地方都可以访问作用域值
System.out.println("服务调用,用户ID: " + USER_ID.get());
System.out.println("请求ID: " + REQUEST_ID.get());
}
}作用域值的优势
- 不可变性:一旦绑定就不能修改
- 自动传递:在调用链中自动传递
- 性能优化:比ThreadLocal 更高效
- 结构化:有明确的作用域边界
结构化并发(第二次孵化器)
Java 20 改进了结构化并发
import jdk.incubator.concurrent.StructuredTaskScope;
public class StructuredConcurrencyExample {
public static void main(String[] args) {
String result = fetchUserProfile(123);
System.out.println(result);
}
public static String fetchUserProfile(int userId) {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 并发获取用户信息
var userTask = scope.fork(() -> fetchUser(userId));
var profileTask = scope.fork(() -> fetchProfile(userId));
var settingsTask = scope.fork(() -> fetchSettings(userId));
// 等待所有任务完成
scope.join();
scope.throwIfFailed();
// 组合结果
User user = userTask.resultNow();
Profile profile = profileTask.resultNow();
Settings settings = settingsTask.resultNow();
return String.format("用户: %s, 个人资料: %s, 设置: %s",
user, profile, settings);
} catch (Exception e) {
return "获取用户信息失败: " + e.getMessage();
}
}
private static User fetchUser(int userId) {
// 模拟数据库查询
simulateDelay(100);
return new User("user" + userId, "user" + userId + "@example.com");
}
private static Profile fetchProfile(int userId) {
simulateDelay(150);
return new Profile("个人简介", "北京");
}
private static Settings fetchSettings(int userId) {
simulateDelay(80);
return new Settings("dark", "zh-CN");
}
private static void simulateDelay(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
record User(String name, String email) {}
record Profile(String bio, String location) {}
record Settings(String theme, String language) {}
