Trang chủHướng dẫnSử dụng ThreadLocal trong Java để tạo biến cục bộ
Java

Sử dụng ThreadLocal trong Java để tạo biến cục bộ

CyStack blog 3 phút để đọc
CyStack blog30/07/2025
Locker Avatar

Bao Tran

Web Developer

Locker logo social
Reading Time: 3 minutes

ThreadLocal trong Java được dùng để tạo ra các biến cục bộ của luồng (thread local variables). Ta biết rằng tất cả các thread của một Object đều chia sẻ chung biến, do đó biến không đảm bảo an toàn đa luồng (thread safe). Ta có thể dùng cơ chế đồng bộ (synchronization) để đảm bảo an toàn cho thread. Nhưng nếu muốn tránh giải pháp đó, ta có thể sử dụng ThreadLocal.

ThreadLocal trong Java

Java ThreadLocal

Mỗi thread sẽ có biến ThreadLocal riêng, và có thể dùng các phương thức get()set() để lấy giá trị mặc định hoặc thay đổi giá trị của biến đó trong phạm vi thread của mình. Các thực thể ThreadLocal thường là các trường mang tính private static trong những class muốn gắn trạng thái với một thread.

Ví dụ ThreadLocal

Đây là một ví dụ nhỏ minh họa cách sử dụng ThreadLocal trong Java và chứng minh rằng mỗi thread có một bản sao riêng của biến ThreadLocal.

File ThreadLocalExample.java:

package com.journaldev.threads;

import java.text.SimpleDateFormat;
import java.util.Random;

public class ThreadLocalExample implements Runnable{

    // SimpleDateFormat is not thread-safe, so give one to each thread
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue()
        {
            return new SimpleDateFormat("yyyyMMdd HHmm");
        }
    };
    
    public static void main(String[] args) throws InterruptedException {
        ThreadLocalExample obj = new ThreadLocalExample();
        for(int i=0 ; i<10; i++){
            Thread t = new Thread(obj, ""+i);
            Thread.sleep(new Random().nextInt(1000));
            t.start();
        }
    }

    @Override
    public void run() {
        System.out.println("Thread Name= "+Thread.currentThread().getName()+" default Formatter = "+formatter.get().toPattern());
        try {
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //formatter pattern is changed here by thread, but it won't reflect to other threads
        formatter.set(new SimpleDateFormat());
        
        System.out.println("Thread Name= "+Thread.currentThread().getName()+" formatter = "+formatter.get().toPattern());
    }

}

Kết quả của chương trình ở trên là:

Thread Name= 0 default Formatter = yyyyMMdd HHmm
Thread Name= 1 default Formatter = yyyyMMdd HHmm
Thread Name= 0 formatter = M/d/yy h:mm a
Thread Name= 2 default Formatter = yyyyMMdd HHmm
Thread Name= 1 formatter = M/d/yy h:mm a
Thread Name= 3 default Formatter = yyyyMMdd HHmm
Thread Name= 4 default Formatter = yyyyMMdd HHmm
Thread Name= 4 formatter = M/d/yy h:mm a
Thread Name= 5 default Formatter = yyyyMMdd HHmm
Thread Name= 2 formatter = M/d/yy h:mm a
Thread Name= 3 formatter = M/d/yy h:mm a
Thread Name= 6 default Formatter = yyyyMMdd HHmm
Thread Name= 5 formatter = M/d/yy h:mm a
Thread Name= 6 formatter = M/d/yy h:mm a
Thread Name= 7 default Formatter = yyyyMMdd HHmm
Thread Name= 8 default Formatter = yyyyMMdd HHmm
Thread Name= 8 formatter = M/d/yy h:mm a
Thread Name= 7 formatter = M/d/yy h:mm a
Thread Name= 9 default Formatter = yyyyMMdd HHmm
Thread Name= 9 formatter = M/d/yy h:mm a

Từ kết quả, ta có thể thấy Thread-0 đã thay đổi giá trị của formatter, nhưng formatter của thread-2 vẫn giữ nguyên giá trị khởi tạo. Ta cũng thấy điều tương tự xảy ra với các thread khác.

Class ThreadLocal trong Java 8 đã được mở rộng với một phương thức mới withInitial(). Nó nhận interface hàm Supplier làm tham số. Do đó, ta có thể dùng biểu thức lambda để tạo thực thể ThreadLocal một cách dễ dàng. Ví dụ, biến ThreadLocal tên formatter ở trên có thể được định nghĩa chỉ trong một dòng như sau:

private static final ThreadLocal<SimpleDateFormat> formatter = 
	ThreadLocal.<SimpleDateFormat>withInitial
	(() -> {return new SimpleDateFormat("yyyyMMdd HHmm");});

Tổng kết

Nếu bạn quan tâm hơn về chi tiết của ThreadLocal, hãy xem thêm tài liệu chính thức để nắm rõ từng phương thức và ứng dụng thực tế. Ngoài ra, bạn cũng có thể khám phá các bài viết khác về Java 8 trên blog này, nhất là chủ đề về interface hàm.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất