ThreadLocal(線程的局部變量)

1.ThreadLocal是Java1.2提出來的一種對線程的所在執行的線程棧的局部變量

這個方式打印出來的就可以說明一個問題,不同的線程他們的的線程棧是不一樣的,換句話當同一個方法被同一個不同的線程調用的時候,他們都會進入各自的線程之間的

棧內存之中。

public class Main {
    public static void main(String[] args) throws Exception {
        log("start main...");
        new Thread(() -> {
            log("run task...");
        }).start();
        new Thread(() -> {
            log("print...");
        }).start();
        log("end main.");
    }

    static void log(String s) {
        System.out.println(Thread.currentThread().getName() + ": " + s);
    }
}

 現象:

main: start main...
Thread-0: run task...
main: end main.
Thread-1: print...

 2.ThreadLocal可以避免在同一個線程之間同一個參數的在不同地方調用(前提是同一個線程),這樣可以避免了同一個參數在同一個線程執行中的多次傳遞;ThreadLocal實例通常總是以靜態字段初始化如下:

static ThreadLocal<User> threadLocalUser = new ThreadLocal<>();

它的典型使用方式如下:

void processUser(user) {
    try {
        threadLocalUser.set(user);
        step1();
        step2();
    } finally {
        threadLocalUser.remove();
    }

通過設置一個User實例關聯到ThreadLocal中,在移除之前,所有方法都可以隨時獲取到該User實例:

void step1() {
    User u = threadLocalUser.get();
    log();
    printUser();
}

void log() {
    User u = threadLocalUser.get();
    println(u.name);
}

void step2() {
    User u = threadLocalUser.get();
    checkUser(u.id);
}

注意到普通的方法調用一定是同一個線程執行的,所以,step1()step2()以及log()方法內,threadLocalUser.get()獲取的User對象是同一個實例。

實際上,可以把ThreadLocal看成一個全局Map<Thread, Object>:每個線程獲取ThreadLocal變量時,總是使用Thread自身作爲key:

Object threadLocalValue = threadLocalMap.get(Thread.currentThread());

 因此,ThreadLocal相當於給每個線程都開闢了一個獨立的存儲空間,各個線程的ThreadLocal關聯的實例互不干擾。最後,特別注意ThreadLocal一定要在finally中清除:

try {
    threadLocalUser.set(user);
    ...
} finally {
    threadLocalUser.remove();
}

 

 這是因爲當前線程執行完相關代碼後,很可能會被重新放入線程池中,如果ThreadLocal沒有被清除,該線程執行其他代碼時,會把上一次的狀態帶進去。爲了保證能釋放ThreadLocal關聯的實例,我們可以通過AutoCloseable接口配合try (resource) {...}結構,讓編譯器自動爲我們關閉。例如,一個保存了當前用戶名的ThreadLocal可以封裝爲一個UserContext對象:

public class UserContext implements AutoCloseable {

    static final ThreadLocal<String> ctx = new ThreadLocal<>();

    public UserContext(String user) {
        ctx.set(user);
    }

    public static String currentUser() {
        return ctx.get();
    }

    @Override
    public void close() {
        ctx.remove();
    }
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章