ThreadLocal學習總結

        ThreadLocal,即線程本地變量,解決線程局部變量統一定義問題,從名稱應該就能看出它是線程的本地變量,是用於線程隔離的 ,而非用於多線程數據共享。題外話,它雖然可以用於隔離線程(純後臺問題),但它不能用於隔離網絡請求即無法分別無狀態的http請求(前臺與後臺交互問題),至於怎麼區分,就是cookie/session/tocken技術的知識了。

一、threadLocal的使用

ThreadLocal類封裝了getMap()、Set()、Get()、Remove()4個核心方法

1.set()方法,通過set(value)的方式將value值存入到 本地變量中,如下3.圖;

.2.get()方法,直接通過get的方式將value值存入到 本地變量中值獲取出來,如下3.圖;

      這裏需要說明一點,雖然threadLocal的存取方式跟集合有些像,但它本身並不是一個集合,更像是 一個基本變量如int之類,只是賦值取值的方式變了,也就是說一個threadLocal變量它本身只能存一個值,當你重新給它set值的時候,之前的值就不存在了,如果你需要在一個線程內存多個變量值的話,需要定義多個threadLocal變量,如3下圖。
3. getMap()(非公共方法不可直接使用,通過get()方法內部調用它),獲取每個子線程Thread持有自己的ThreadLocalMap實例(即如上述所說,如果你定義多個本地變量,可以從這裏全部獲取), 因此它們是不存在併發競爭的。可以理解爲每個線程有自己的變量副本。通俗的講,當你在同一個線程內定義了多個本地變量時,這些threadLocal變量會存到當前線程的threadLocalMap變量中,ThreadLocal可以通過該方法獲取所有本地線程變量,如下threadLocal1與threadLocal2爲同一個線程內的變量,則存儲在同一個ThreadLocalMap中,threadLocal內部獲取的時候通過ThreadLocalMap map = getMap(Thread.currentThread())獲取到 當前線程的所有本地變量,並通過遍歷map或傳入threadLocal變量名獲得,如內部獲取時 ThreadLocalMap.Entry e = map.getEntry(this);

		ThreadLocal<String> threadLocal1=new ThreadLocal<>();
		ThreadLocal<String> threadLocal2=new ThreadLocal<>();
		String th1="ceshi1";
		String th2="ceshi2";
		threadLocal1.set(th1);
		threadLocal1.get();
		threadLocal2.set(th2);
		threadLocal2.get();

以上是它的主要使用方法 ,至於ThreadLocal,到底是怎麼把線程進行隔離的,只需要解析 一下它的源碼即可。

源碼按順序主要涉及:

1,ThreadLocal類的set()方法 

2,Thread類的threadLocalMap變量,

3,ThreadLocal類的get()方法,

ThreadLocal類的set()方法,如下所示,thread在調用set方法的時候獲取到當前線程,並通過threadLocal類的getMap()方法,獲取到當前線程的threadLocalMap變量,然後以當前threadLocal實例作爲key,將值存入到threadLocalMap中,threadLocalMap

    /**
     * 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爲threadLocal的一個內部類,它也有一個set()方法,該方法的作用主要是將傳進來的threadLocal實例作爲key,傳進來的value作爲value存入到Entry[]數組中,如下:

        /**
         * Set the value associated with key.
         *
         * @param key the thread local object
         * @param value the value to be set
         */
        private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

最後通過threadLocal的get()方法獲取到當前現成的本地變量,如下:

    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    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();
    }

該方法,跟set方法類似,同樣是先獲得當前線程。並通過getMap方法獲取到當前線程的ThreadLocalMap變量,並已當前threadLocal實例作爲key通過調用ThreadLocalMap的getEntry()方法(可以看到比對了key值,即比對了是否是同一個 threadLocal實例)獲取到內部的Entry

        /**
         * Get the entry associated with key.  This method
         * itself handles only the fast path: a direct hit of existing
         * key. It otherwise relays to getEntryAfterMiss.  This is
         * designed to maximize performance for direct hits, in part
         * by making this method readily inlinable.
         *
         * @param  key the thread local object
         * @return the entry associated with key, or null if no such
         */
        private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }

以上既是ThreadLocal從設置值到取到值的過程,整體來講思路就是,將ThreadLocal 實例作爲key並將set進實例的值作爲value存入到當前線程中一個變量中(ThreadLocalMap),也就是誰的變量誰來保存,也就不會出現線程之間共享的問題了,取得時候再通過threadLocal的get方法從當前線程中再把它拿出來即可。

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