
Scoped Values เบื้องต้น: ส่งบริบทแบบ immutable ในขอบเขตเดียว
เวลาอยากส่ง requestId หรือสิทธิ์ผู้ใช้ลงงานลูกโดยไม่โยงพารามิเตอร์ทุกเมธอด คนมักใช้ ThreadLocal ครับ แต่กับ virtual threads จำนวนมาก การคัดลอก/ล้างค่าผิดจุดทำให้รั่วหรือกินหน่วยความจำง่าย
Scoped Values (ดู JEP 506) เป็นค่า immutable ที่ผูกกับ ขอบเขตแบบ structured แล้วส่งต่อให้งานลูกในกรอบนั้น โดยไม่ต้องให้ทุกเธรดมีสำเนาที่เขียนทับได้
แนวคิดสั้น ๆ
- ประกาศคีย์เป็น
static final ScopedValue<...> - ผูกค่าด้วย
ScopedValue.where(KEY, value).run(() -> { ... })(หรือ API เทียบเท่าตามรุ่น) - อ่านด้วย
KEY.get()ภายในขอบเขตเท่านั้น - นอกขอบเขตต้องไม่พึ่งค่าเดิม
ต่างจาก ThreadLocal ที่มัก mutable และอายุยืนตามเธรด Scoped Value ตั้งใจให้สั้นและอ่านอย่างเดียวในกรอบ
ตัวอย่าง static-only (แนวคิด)
บล็อกด้านล่างเป็น fragment สำหรับอ่านแนวคิด ไม่ได้ตั้งเป็น verify suite เพราะ API สุดท้ายขึ้นกับรุ่น JDK ที่ทีมใช้ (preview → final ตาม JEP)
// static-only fragment — conceptual ScopedValue usage
import java.lang.ScopedValue;
public final class RequestScope {
public static final ScopedValue<String> REQUEST_ID = ScopedValue.newInstance();
private RequestScope() {}
public static void handle(String requestId, Runnable work) {
ScopedValue.where(REQUEST_ID, requestId).run(work);
}
public static String currentRequestId() {
return REQUEST_ID.get();
}
}
เมื่อไหร่ควรย้ายจาก ThreadLocal
- ส่ง context ของ request / trace id ลง Structured Concurrency
- ต้องการค่าที่ห้ามเขียนทับกลางทาง
- ใช้ virtual threads จำนวนมากแล้วกลัว ThreadLocal บloat
ยังไม่ต้องรีบไล่แทนที่ทุก ThreadLocal ที่เป็น cache ของไลบรารีเก่า ให้เริ่มจาก context ของแอปคุณเอง
Recap
- ScopedValue = บริบท immutable ในขอบเขตชัด
- ประกาศ static final แล้วผูกด้วย
where(...).run(...) - เหมาะกับ request context มากกว่าแคช mutable
- เช็กรุ่น JDK / JEP 506 ก่อนเปิดในโปรดักชันครับ