JEP 464:范围值(第二预览版)
概括
引入_作用域值_,它允许与同一线程中的子帧以及子线程管理不可变数据的共享。作用域值比线程局部变量更容易推理,并且空间和时间成本更低,特别是与虚拟线程和结构化并发结合使用时。这是一个预览 API。
历史
范围值通过JEP 429在 JDK 20 中孵化,并通过JEP 446成为 JDK 21 中的预览 API 。我们在此建议在 JDK 22 中重新预览 API,无需进行任何更改,以获得额外的经验和反馈。
目标
-
易于使用——应该很容易推理数据流。
-
可理解性——共享数据的生命周期从代码的语法结构中是可见的。
-
鲁棒性——调用者共享的数据只能由合法的被调用者检索。
-
性能——数据可以在大量线程之间有效地共享。
非目标
-
改变 Java 编程语言并不是目标。
-
要求迁移线程局部变量或弃用现有 API 并不是我们的目标
ThreadLocal
。
动机
Java 应用程序和库被构造为包含方法的类的集合。这些方法通过方法调用进行通信。
大多数方法允许调用者通过将数据作为参数传递来将数据传递给方法。当方法A
希望方法B
为它做一些工作时,它会B
使用适当的参数进行调用,并且B
可能会将其中一些参数传递给等等C
。B
可能必须在其参数列表中不仅包括直接需要的东西,而且还包括必须传递的B
东西B
到C
。例如,如果B
要设置并执行数据库调用,则可能需要传入一个 Connection,即使B
不直接使用 Connection。
大多数时候,这种“传递间接被调用者所需的内容”的方法是共享数据的最有效和最方便的方法。然而,有时传递每个间接被调用者在初始调用中可能需要的所有数据是不切实际的。
一个例子
将控制从一个组件(“框架”)转移到另一个组件(“应用程序代码”)然后再返回,这是大型 Java 程序中的一种常见模式。例如,Web 框架可以接受传入的 HTTP 请求,然后调用应用程序处理程序来处理它。然后,应用程序处理程序可以调用框架将数据读取到数据库或调用某些其他 HTTP 服务。
@Override
public void handle(Request request, Response response) { // user code; called by framework
...
var userInfo = readUserInfo();
...
}
private UserInfo readUserInfo() {
return (UserInfo)framework.readKey("userInfo", context);// call framework
}
框架可以维护一个FrameworkContext
对象,其中包含经过验证的用户ID、交易ID等,并将其与当前交易相关联。所有框架操作都使用该FrameworkContext
对象,但用户代码不使用该对象(并且与用户代码无关)。
实际上,框架必须能够将其内部上下文从其serve
方法(调用用户的handle
方法)传递到其readKey
方法:
4. Framework.readKey <--------+ use context
3. Application.readUserInfo |
2. Application.handle |
1. Framework.serve ----------+ create context
最简单的方法是将对象作为参数传递给调用链中的所有方法:
@Override
void handle(Request request, Response response, FrameworkContext context) {
...
var userInfo = readUserInfo(context);
...
}
private UserInfo readUserInfo(FrameworkContext context) {
return (UserInfo)framework.readKey("userInfo", context);
}
用户代码无法_协助_正确处理上下文对象。在最坏的情况下,它可能会因混淆上下文而产生干扰;最好的情况是,它需要向所有可能最终回调到框架的方法添加另一个参数。如果在重新设计框架期间需要传递上下文,那么添加它不仅需要直接客户端(直接调用框架方法或直接由框架方法调用的用户方法)更改其签名,还需要更改所有中间方法,例如好吧,即使上下文是框架的内部实现细节,用户代码也不应该与其交互。