Java 的作用域值(Scoped Values)和結構化并發(Structured Concurrency)
以下代碼在Java 24 preview測試通過
不使用虛擬線程
package org.example;
import java.util.concurrent.*;
public class ScopedValueWithVirtualThreads {
void main() {
final String THREAD_S_N = "%s read USER_ID = %s, Thread: %s%n";
final ScopedValue<String> USER_ID = ScopedValue.newInstance();
// 將 ScopedValue 綁定到當前線程作用域
ScopedValue.where(USER_ID, "user-123").run(() -> {
try (var scope = new StructuredTaskScope.ShutdownOnFailure("Thread", Executors.defaultThreadFactory())) {
// 在子線程中讀取 ScopedValue(會繼承父線程的綁定)
var f1 = scope.fork(() -> String.format(THREAD_S_N, "Task1", USER_ID.get(), Thread.currentThread()));
var f2 = scope.fork(() -> String.format(THREAD_S_N, "Task2", USER_ID.get(), Thread.currentThread()));
scope.join();
scope.throwIfFailed();
System.out.printf(f1.get());
System.out.printf(f2.get());
System.out.printf(THREAD_S_N, "In-try", USER_ID.get(), Thread.currentThread());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
}
}
輸出結果:
Task1 read USER_ID = user-123, Thread: Thread[#26,pool-1-thread-1,5,main]
Task2 read USER_ID = user-123, Thread: Thread[#27,pool-1-thread-2,5,main]
In-try read USER_ID = user-123, Thread: Thread[#3,main,5,main]
使用虛擬線程
package org.example;
import java.util.concurrent.*;
public class ScopedValueWithVirtualThreads {
void main() {
final String THREAD_S_N = "%s read USER_ID = %s, Thread: %s%n";
final ScopedValue<String> USER_ID = ScopedValue.newInstance();
// 將 ScopedValue 綁定到當前線程作用域
ScopedValue.where(USER_ID, "user-123").run(() -> {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 在子線程中讀取 ScopedValue(會繼承父線程的綁定)
var f1 = scope.fork(() -> String.format(THREAD_S_N, "Task1", USER_ID.get(), Thread.currentThread()));
var f2 = scope.fork(() -> String.format(THREAD_S_N, "Task2", USER_ID.get(), Thread.currentThread()));
scope.join();
scope.throwIfFailed();
System.out.printf(f1.get());
System.out.printf(f2.get());
System.out.printf(THREAD_S_N, "In-try", USER_ID.get(), Thread.currentThread());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
}
}
輸出結果:
Task1 read USER_ID = user-123, Thread: VirtualThread[#27]/runnable@ForkJoinPool-1-worker-1
Task2 read USER_ID = user-123, Thread: VirtualThread[#29]/runnable@ForkJoinPool-1-worker-1
In-try read USER_ID = user-123, Thread: Thread[#3,main,5,main]
解釋
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {- 創建一個 結構化任務作用域 scope,策略是 ShutdownOnFailure。
- 含義:如果任何一個子任務拋出異常,自動取消其他所有子任務。
- 自動資源管理:scope.close() 會在 try 塊結束時被調用,做清理工作。
scope.fork(...),使用 scope.fork() 啟動一個新的子任務scope.join();,等待所有子任務完成(不論成功還是失敗)。- 注意:這不會拋出異常,只是阻塞當前線程直到所有任務都完成。
scope.throwIfFailed();,如果有任何子任務拋出異常,就把它的異常重新拋出,其他任務已被取消。- ShutdownOnFailure 策略保證了只要有失敗,就立即停止其他任務的運行(或阻止它們開始)。
什么是作用域值
- 在父線程中使用 ScopedValue.where() 綁定了 USER_ID = "user-123";
- 子線程是通過 StructuredTaskScope.fork() 啟動的,它們自動繼承了這個綁定;
- 所以在 f1 和 f2 中訪問 USER_ID.get() 會得到 "user-123",不需要重新傳參;
- 這種繼承是線程安全的、只讀的,不像 ThreadLocal 容易被污染或修改。

浙公網安備 33010602011771號