1. 程式人生 > >Java ThreadLocal 使用及實現原理

Java ThreadLocal 使用及實現原理

一. ThreadLocal是什麼

ThreadLocal 是執行緒本地資料儲存類,通過ThreadLocal可以在特定的執行緒中儲存資料和變數, 並且這些資料之後只能由該執行緒訪問,其他執行緒是訪問不了的, 保證各個執行緒裡資料和變數的獨立性; 即ThreadLocal使每個執行緒可以訪問自己內部的副本變數。

二. 通過例子來了解ThreadLocal

下面通過一個簡單的例子來說明ThreadLocal的作用以及使用;
程式碼如下:

public class ThreadLocalDemo {

    private static ThreadLocal<String> mThreadLocalDesc = new
ThreadLocal<>(); public static class Thread1 extends Thread { @Override public void run() { super.run(); mThreadLocalDesc.set("this is thread-1"); System.out.println("Thread1 mThreadLocalDesc = "+mThreadLocalDesc.get()); } } public
static class Thread2 extends Thread { @Override public void run() { super.run(); mThreadLocalDesc.set("this is thread-2"); System.out.println("Thread2 mThreadLocalDesc = "+mThreadLocalDesc.get()); } } public static void main(String[] args) { mThreadLocalDesc.set("this is main thread"
); System.out.println("main mThreadLocalDesc = "+mThreadLocalDesc.get()); Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); thread1.start(); thread2.start(); } }

我們分別使用ThreadLocal來在Thread1, Thread2 和 main執行緒這三個執行緒裡設定相應的變數, 並且都打印出來,檢驗是否儲存了執行緒各自變數的值, 程式執行結果如下:
這裡寫圖片描述

三.原始碼解析ThreadLocal類

實現思想,每個執行緒維護一個 ThreadLocalMap 的對映表,對映表的 keyThreadLocal 例項本身,value 是要儲存的副本變數。ThreadLocal 例項本身並不儲存值,它只是提供一個在當前執行緒中找到副本值的 key。

ThreadLocal 類定義如下:

public class ThreadLocal<T> 

從類的宣告可以看出ThreadLocal是一個泛型類, 所以它可以用來儲存任何型別的執行緒本地資料;

ThreadLocal類提供的幾個方法:

public T get() { }
get 方法是用來獲取 ThreadLocal 在當前執行緒中儲存的變數副本

public void set(T value) { }
set 用來設定當前執行緒中變數的副本

public void remove() { }
remove 用來移除當前執行緒中變數的副本

protected T initialValue() { }
initialValue 是一個protected方法,一般是用來在使用時進行重寫的,做初始化操作

1. 先分析set方法的實現:

public void set(T value) {
    //取得當前執行緒
    Thread t = Thread.currentThread();
    //獲取當前執行緒 ThreadLocalMap 
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

Thread類的threadLocals欄位定義如下:

//與此執行緒相關的ThreadLocal值。這個對映由ThreadLocal類維護。
ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocalMap 來儲存每個執行緒副本變數,它是ThreadLocal 裡的一個靜態內部類。ThreadLocalMap 也是採用的散列表(Hash)思想來實現的

ThreadLocalMap的實現方式:

Map 是一種 key-value 形式的資料結構,ThreadLocalMap 使用 Entry 類來儲存資料,下面是Entry類的定義:

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
         super(k);
         value = v;
    }
}

EntryThreadLocal 例項作為 key,副本變數作為 value 儲存起來, Entry 中對於 ThreadLocal 例項的引用是一個弱引用;

整個 set 方法就是先取得當前執行緒,然後通過 getMap(t) 方法獲取到一個MapMap的型別為ThreadLocalMap, 如果此時獲取到的map = null,就是先建立ThreadLocalMap, 否則就儲存該變數, 注意這裡獲取鍵值對傳進去的是 this,而不是當前執行緒t.

2. 再分析get方法的實現:

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

get方法也是先獲取當前執行緒, 然後再獲取ThreadLocalMap, 通過ThreadLocalMapgetEntry(this)直接獲取value;
如果在map中查詢不到對應的儲存,則會通過呼叫setInitialValue方法返回初始化時的初始值,而預設情況下,initialValue方法返回的是null。