Java 25 ScopedValue - 作用域内安全访问的一种实现
1、ScopedValue 简述
ScopedValue基本用法:定义一个ScopedValue,使用ScopedValue.where()方法创建一个作用域,在该作用域内可以安全地访问ScopedValue的值,当执行离开作用域时,值会自动解除绑定。
2、ScopedValue 优势
- 相比
ThreadLocal的优势在于:
- 更清晰的变量生命周期管理
- 减少了内存泄漏的风险
- 更好的类型安全性
- 更适合现代并发编程模型
3.1、基本用法示例
java
public class ScopedValueExample {
// 定义一个 ScopedValue String 类型
private static final ScopedValue<String> USER_NAME = ScopedValue.newInstance();
// 定义一个 ScopedValue Map 类型
private static final ScopedValue<Map<String, Integer>> USER_MAP = ScopedValue.newInstance();
public static void main(String[] args) {
// 在作用域内设置值 String 类型
ScopedValue.where(USER_NAME, "就不告诉你")
.run(() -> {
// 在这个作用域内可以访问USER_NAME
System.out.println("用户名: " + USER_NAME.get());
// 可以调用其他方法,值会自动传递
processData();
});
// 在作用域内设置值 Map 类型
Map<String, Integer> userMap = new HashMap<>();
userMap.put("小明", 28);
userMap.put("小黑", 25);
ScopedValue.where(USER_MAP, userMap)
.run(() -> {
// 在这个作用域内可以访问 USER_MAP 信息
System.out.println("小明年龄: " + userMap.get("小明"));
// 可以调用其他方法,值会自动传递
processMapData();
});
// 作用域外无法访问
// System.out.println(USER_NAME.get()); // 会抛出IllegalStateException
System.out.println(USER_MAP.get().get("小明")); // 会抛出IllegalStateException
}
private static void processMapData() {
// 可以在子方法中访问 ScopedValue
System.out.println("子方法中获取小黑年龄: " + USER_MAP.get().get("小黑"));
}
private static void processData() {
// 可以在子方法中访问 ScopedValue
System.out.println("子方法中获取用户名: " + USER_NAME.get());
}
}
3.2 优势
- 无需手动
remove(),作用域结束自动清理。
与虚拟线程兼容,支持结构化并发。
4.1 线程池场景示例
java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExample {
static final ScopedValue<Integer> TASK_ID = ScopedValue.newInstance();
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(5);
// ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
// 作用域内自动管理生命周期
ScopedValue.where(TASK_ID, taskId)
.run(() -> {
System.out.println("Task " + TASK_ID.get() + " running");
// 业务逻辑
// 可以调用其他方法,值会自动传递
doSomethings();
});
});
}
executor.shutdown();
// Executors.newVirtualThreadPerTaskExecutor(); 执行时需添加,目的是为了等待线程执行完成
// executor.awaitTermination(1, TimeUnit.MINUTES);
}
private static void doSomethings() {
System.err.println(TASK_ID.get() + " doSomethings");
}
}
4.2 优势
- 避免线程池复用导致的数据串扰问题。
作用域结束自动清理,无需显式remove()。
5.1 结构化并发示例
java
import java.util.concurrent.*;
import java.time.Duration;
public class ScopedConcurrencyExample {
// 定义业务上下文变量(相当于动态作用域变量)
private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
private static final ScopedValue<Duration> REQUEST_TIMEOUT = ScopedValue.newInstance();
public static void main(String[] args) {
final long startTime = System.currentTimeMillis();
// 绑定上下文变量(会在所有子任务中自动继承)
ScopedValue.where(USER_ID, "usr-20260106")
.where(REQUEST_TIMEOUT, Duration.ofSeconds(3))
.run(() -> processOrder(startTime));
}
private static void processOrder(long requestStartTime) {
try (var scope = StructuredTaskScope.open()) {
// 在相同作用域中启动并发任务
StructuredTaskScope.Subtask<String> fetchInventory = scope.fork(() -> getInventory());
StructuredTaskScope.Subtask<Integer> calcPayment = scope.fork(() -> calculatePayment(requestStartTime));
// 模拟系统错误
// scope.close();
StructuredTaskScope.Subtask<String> genInvoice = scope.fork(() -> generateInvoice());
// 等待所有任务完成
scope.join();
// 任务完成后的结果处理
System.out.println(
"订单处理结果:\n" +
"用户: " + USER_ID.get() + "\n" +
"库存状态: " + fetchInventory.get() + "\n" +
"支付金额: $" + (calcPayment.get() / 100.0) + "\n" +
"发票: " + genInvoice.get().replace("\n", "\n ")
);
} catch (InterruptedException e) {
System.err.println("订单处理异常: " + e.getMessage());
Thread.currentThread().interrupt();
} catch (Exception e) {
System.err.println("系统错误: " + e.getMessage());
}
}
private static String getInventory() {
System.out.println("获取库存 (用户: " + USER_ID.get() + ")");
simulateWork(800);
return "SKU-3456 库存充足";
}
private static Integer calculatePayment(long start) {
Duration remaining = REQUEST_TIMEOUT.get().minusMillis(System.currentTimeMillis() - start);
if (remaining.isNegative()) {
throw new RuntimeException("支付计算超时");
}
System.out.println("计算支付 (剩余时间: " + remaining.toMillis() + "ms)");
simulateWork(1200);
return 4599;
}
private static String generateInvoice() {
System.out.println("生成发票 (用户: " + USER_ID.get() + ")");
simulateWork(1500);
return
"发票编号: INV-" + USER_ID.get() + "-20260106\n" +
"---------------------------------------\n" +
"项目: 结构化并发服务包\n" +
"金额: 4599.00";
}
private static void simulateWork(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("任务中断");
}
}
}
5.2 优势
- 与虚拟线程天然兼容,支持嵌套作用域。
子任务自动继承父作用域的值,无需手动传递参数。
6.1 高级特性-条件绑定与错误处理
java
import java.lang.ScopedValue;
public class AdvancedFeatures {
static final ScopedValue<String> MODE = ScopedValue.newInstance();
public static void main(String[] args) {
// 条件绑定
ScopedValue.where(MODE, "production")
.run(() -> {
if ("production".equals(MODE.get())) {
System.out.println("Running in production mode");
} else {
System.out.println("Running in test mode");
}
});
// 错误处理
try {
ScopedValue.where(MODE, "test")
.run(() -> {
throw new RuntimeException("Simulated error");
});
} catch (RuntimeException e) {
System.out.println("Caught exception: " + e.getMessage());
}
}
}
6.2 优势
- 支持条件绑定,根据值执行不同逻辑。
异常处理时自动清理作用域,无需额外操作。
7. 总结
copedValue通过作用域管理机制,解决了ThreadLocal的内存泄漏和数据串扰问题,尤其适用于虚拟线程和结构化并发场景。其自动清理特性显著简化了并发编程,提升了代码安全性和可维护性。
安装包路径
Open JDK 25:https://jdk.java.net/25/