请求上下文(用户、trace id)放在 ThreadLocal 里,一进线程池就丢了?InheritableThreadLocal 为什么复用线程时拿到旧值?ScopedValue 和结构化并发怎么传?这篇逐个跑出来,还用了真实的 TTL 库。
1 新建线程:TL=null ITL=req-1
2 线程池第一次任务(线程此刻才被创建):TL=null ITL=req-1
3 main 改成 req-2 后,线程池第二次任务(复用同一线程):TL=null ITL=req-1
4 另一个新池,线程创建时 main 的 ITL=req-3:TL=null ITL=req-3
5 main 移除之后,线程池 2 的线程里 ITL 还是:TL=null ITL=req-3
6 子线程改它自己的副本后,main 的 ITL = null
7 虚拟线程(继承默认开):TL=null ITL=req-7
8 虚拟线程(inheritInheritableThreadLocals(false)):TL=null ITL=null 任务看到 TL=req-A
任务看到 TL=req-B
没有包装的任务看到 TL=null(包装版本在结束时已经恢复) JDK 21:
编译失败:
<jtmp>/j152c/Main.java:3: error: ScopedValue is a preview API and is disabled by default.
static final ScopedValue<String> USER = ScopedValue.newInstance();
^
(use --enable-preview to enable preview APIs)
<jtmp>/j152c/Main.java:3: error: ScopedValue is a preview API and is disabled by default.
static final ScopedValue<String> USER = ScopedValue.newInstance();
^
(use --enable-preview to enable preview APIs)
<jtmp>/j152c/Main.java:6: error: ScopedValue is a preview API and is disabled by default.
ScopedValue.where(USER, "alice").run(() -> {
^
(use --enable-preview to enable preview APIs)
…(共 29 行)
JDK 21 + --enable-preview:
isBound(外面)=false;orElse=(无)
里面 get()=alice
嵌套重新绑定 get()=bob
嵌套结束后 get()=alice
普通子线程 isBound=false
(没有异常)
外面 get():java.util.NoSuchElementException
where().call 返回值:CAROL
ScopedValue 没有 set 方法(绑定不可变,只能用 where 重新绑定)
JDK 27:
isBound(外面)=false;orElse=(无)
里面 get()=alice
嵌套重新绑定 get()=bob
嵌套结束后 get()=alice
普通子线程 isBound=false
(没有异常)
外面 get():java.util.NoSuchElementException: ScopedValue not bound
where().call 返回值:CAROL
ScopedValue 没有 set 方法(绑定不可变,只能用 where 重新绑定) JDK 21 + --enable-preview:
子任务看到 USER=alice,线程是虚拟线程=true
JDK 27:
编译失败:
<jtmp>/j152d/Main.java:6: error: StructuredTaskScope is a preview API and is disabled by default.
try (var scope = StructuredTaskScope.open()) {
^
(use --enable-preview to enable preview APIs)
1 error
JDK 27 + --enable-preview:
子任务看到 USER=alice,线程是虚拟线程=true main 设置 req-1:未包装的池 ITL=null;TtlExecutors 包装的池 TTL=req-1
main 设置 req-2:未包装的池 ITL=null;TtlExecutors 包装的池 TTL=req-2
TtlRunnable 在线程 pool-1-thread-1 里看到 req-3
TtlRunnable 执行完之后,未包装的池线程里 TTL 是:null(执行结束后被恢复);main 里是 req-4