四、ThreadLocal

一、與synchronized關(guān)鍵字對比

都可以實現(xiàn)多線程之間的共享。synchronized是利用鎖的機制,使變量或代碼塊在同一個時刻只能一個線程去訪問。而ThreadLocal,字面意思是“線程本地”,為每一個線程提供了一個單獨的變量的副本,每個線程同一時刻訪問的不是同一個對象而是每個線程鎖擁有的獨特的對象。ThreadLocal在開源框架(Spring)中用的非常的多。

二、使用ThreadLocal的四個方法

/**
 *類說明:演示ThreadLocal的使用
 */
public class UseThreadLocal {
    
    private static ThreadLocal<Integer> intLocal
            = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };

    private static ThreadLocal<String> stringThreadLocal;

    /**
     * 運行3個線程
     */
    public void StartThreadArray(){
        Thread[] runs = new Thread[3];
        for(int i=0;i<runs.length;i++){
            runs[i]=new Thread(new TestThread(i));
        }
        for(int i=0;i<runs.length;i++){
            runs[i].start();
        }
    }
    
    /**
     *類說明:測試線程,線程的工作是將ThreadLocal變量的值變化,并寫回,看看線程之間是否會互相影響
     */
    public static class TestThread implements Runnable{
        int id;
        public TestThread(int id){
            this.id = id;
        }
        public void run() {
            System.out.println(Thread.currentThread().getName()+":start");
            Integer s = intLocal.get();
            s = s+id;
            intLocal.set(s);
            System.out.println(Thread.currentThread().getName()
                    +":"+ intLocal.get());
            //intLocal.remove();
        }
    }

    public static void main(String[] args){
        UseThreadLocal test = new UseThreadLocal();
        test.StartThreadArray();
    }
}

1、初始化

 private static ThreadLocal<Integer> intLocal
            = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };

2、設(shè)置值

intLocal.set(s);

3、取值

intLocal.get()

4、移除

intLocal.remove()

三、實現(xiàn)原理

從ThreadLocal的set方法說起,set是用來設(shè)置想要在線程本地的數(shù)據(jù),可以看到先拿到當(dāng)前線程,然后獲取當(dāng)前線程的ThreadLocalMap,如果map不存在先創(chuàng)建map,然后設(shè)置本地變量值。

 /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

那ThreadLocalMap又是什么?跟線程有什么關(guān)系?可以看到ThreadLocalMap其實是線程自身的一個成員屬性threadLocals的類型。也就是線程本地數(shù)據(jù)都存在這個threadLocals應(yīng)用的ThreadLocalMap中。

    /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

ThreadLocalMap是屬于thread類的,也就是說ThreadLocalMap是Thread類的一個成員變量,一個線程new出來之后,每個線程內(nèi)部都有一個獨立的threadLocals。


Thread類的內(nèi)部成員變量

再來看看ThreadLocalMap這個類,內(nèi)部還有一個entry的內(nèi)部類,這個類其實就是一個map的定義,以ThreadLocal為鍵,以我們要保存的值為value。


ThreadLocalMap

其內(nèi)部還有個Entry數(shù)組,將數(shù)據(jù)包裝成靜態(tài)內(nèi)部類Entry對象,存儲在這個table數(shù)組中,數(shù)組的下標(biāo)是threadLocal的threadLocalHashCode&(INITIAL_CAPACITY-1),因為數(shù)組的大小是2的n次方,那其實這個值就是threadLocalHashCode%table.length,用&而不用%,其實是提升效率。只要數(shù)組的大小不變,這個索引下標(biāo)是不變的,這也方便去set和get數(shù)據(jù)。

         /**
         * Construct a new map initially containing (firstKey, firstValue).
         * ThreadLocalMaps are constructed lazily, so we only create
         * one when we have at least one entry to put in it.
         */
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

其get方法也更為簡單,拿著當(dāng)前線程的threadlocalmap成員變量去找相應(yīng)的數(shù)據(jù)

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();
    }

圖示:


threadlocal實現(xiàn)原理

看到這里大家應(yīng)該就明白了,每個線程自身都維護著一個ThreadLocalMap,用來存儲線程本地的數(shù)據(jù),可以簡單理解成ThreadLocalMap的key是ThreadLocal變量,value是線程本地的數(shù)據(jù)。就這樣很簡單的實現(xiàn)了線程本地數(shù)據(jù)存儲和交互訪問。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容