99 Ngày Java — Ngày 69: ThreadLocal và bất biến
ThreadLocal cho mỗi thread một giá trị riêng, hữu ích ở ranh giới request nhưng dễ bị hiểu nhầm là nơi lưu dữ liệu toàn cục. Trong thread pool, worker được tái sử dụng nên dữ liệu cũ có thể xuất hiện ở tác vụ sau nếu không dọn. Đối tượng bất biến thường làm việc truyền dữ liệu giữa các luồng an toàn và dễ hiểu hơn.

1. ThreadLocal có phạm vi một luồng
set và get thao tác trên giá trị của thread hiện tại, không truyền sang thread khác. Gọi remove trong finally để tránh dữ liệu request trước còn trên worker tái sử dụng. Không đặt token hoặc object graph lớn vào ThreadLocal lâu sống; leak có thể giữ dữ liệu và bộ nhớ lâu hơn mong muốn.
private static final ThreadLocal<String> REQUEST_ID =
new ThreadLocal<>();
try {
REQUEST_ID.set(requestId);
handleRequest();
} finally {
REQUEST_ID.remove();
}2. Async và virtual thread không tự kế thừa
Một task chạy trên executor khác không thấy ThreadLocal của caller. Đừng dựa vào InheritableThreadLocal để truyền context chung qua pool vì dữ liệu có thể sai thời điểm. Truyền requestId như tham số, hoặc dùng cơ chế context propagation được kiểm soát và dọn đúng vòng đời.
record Work(String requestId, Payload payload) {}
executor.submit(() -> process(new Work(requestId, payload)));3. Immutable value giảm nhu cầu khóa
Record, final field và defensive copy giúp một giá trị không đổi sau khi tạo. List.copyOf chặn sửa qua danh sách gốc và trả danh sách không hỗ trợ sửa, nhưng không tự sao chép sâu phần tử mutable. Nếu DTO chứa Date hoặc object có setter, cần sao chép hoặc thay bằng kiểu bất biến.
record Batch(List<String> ids) {
Batch { ids = List.copyOf(ids); }
}4. Scoped Values trên Java 21 là preview
ScopedValue đưa dữ liệu chỉ đọc theo phạm vi động và được thiết kế để phối hợp tốt hơn với virtual threads. Nhưng JEP 446 trong Java 21 vẫn là preview, cần --enable-preview khi dùng. Với ứng dụng cần Java 21 ổn định không bật preview, ưu tiên truyền tham số tường minh; chỉ dùng ThreadLocal khi thật cần context gắn luồng.
Kiểm tra mọi set() có finally/remove() tương ứng.
Ưu tiên giá trị bất biến khi giao dữ liệu cho task khác.
ThreadLocal là công cụ theo luồng, không phải cơ chế chia sẻ ngầm. Dọn giá trị, truyền dữ liệu rõ ràng và ưu tiên bất biến. Ngày 70 tổng kết cách chọn công cụ concurrency.
Tài liệu đối chiếu
https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/ThreadLocal.html
https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/List.html#copyOf(java.util.Collection)
https://openjdk.org/jeps/446
Site Admin
Engineer and writer. Building things with TypeScript and distributed systems.
Bình luận (0)
Bạn cần đăng nhập bằng Google để bình luận.
Hãy là người bình luận đầu tiên.


