日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

一、ThreadLocal的原理

ThreadLocal是一個(gè)非常重要的類,它為每個(gè)線程提供了一個(gè)獨(dú)立的變量副本。因此,每個(gè)線程都可以獨(dú)立地訪問(wèn)和修改該變量,而不會(huì)影響其他線程的訪問(wèn)。這種機(jī)制在多線程編程中非常有用,特別是在需要處理線程安全問(wèn)題時(shí)。

ThreadLocal的原理很簡(jiǎn)單:它為每個(gè)線程維護(hù)一個(gè)Map,該Map中存儲(chǔ)了每個(gè)線程對(duì)應(yīng)的變量值。當(dāng)我們調(diào)用ThreadLocal的get()方法時(shí),它將先獲取當(dāng)前線程,然后從當(dāng)前線程的Map中查找對(duì)應(yīng)的變量;如果該變量不存在,那么就通過(guò)initialValue()方法來(lái)創(chuàng)建一個(gè)新的變量,并將其存儲(chǔ)到當(dāng)前線程的Map中。在以后的訪問(wèn)中,該線程便可以直接從Map中獲取變量值,而不需要通過(guò)參數(shù)傳遞或共享變量的方式。

二、源碼關(guān)鍵片段講解

1.get方法

用于獲取當(dāng)前線程的 ThreadLocal 對(duì)象所對(duì)應(yīng)的值

    /**
     * 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() {
        // 通過(guò) Thread.currentThread() 方法獲取當(dāng)前線程對(duì)象 t
        Thread t = Thread.currentThread();
        // 通過(guò) getMap(t) 方法獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象 map
        ThreadLocalMap map = getMap(t);
        // 如果當(dāng)前線程已經(jīng)存儲(chǔ)了該 ThreadLocal 對(duì)象對(duì)應(yīng)的值,那么就通過(guò) map.getEntry(this) 方法得到該值的 Entry 對(duì)象 e,并返回其 value 屬性即可
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        // 否則,需要進(jìn)行初始化,并將初始化后的值保存到當(dāng)前線程中。這里調(diào)用的是 setInitialValue() 方法,其實(shí)現(xiàn)在類的另一個(gè)方法中。
        return setInitialValue();
    }

2.setInitialValue方法

用于初始化當(dāng)前線程的 ThreadLocalMap 對(duì)象并將初始化后的值保存到其中。

    /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    private T setInitialValue() {
        // 調(diào)用 initialValue() 方法獲取 ThreadLocal 對(duì)象的初始值
        T value = initialValue();
        // 通過(guò) Thread.currentThread() 方法獲取當(dāng)前線程對(duì)象 t
        Thread t = Thread.currentThread();
        // 通過(guò) getMap(t) 方法獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象 map
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            // 如果 map 不為空,就使用 map.set(this, value) 方法
            // 將 ThreadLocal 對(duì)象和其對(duì)應(yīng)的值存儲(chǔ)到 map 中,
            // 即將該對(duì)象和值作為 key-value 存入 map 的 table 數(shù)組的某個(gè)位置,
            // table 的索引通過(guò) hashCode() 方法進(jìn)行計(jì)算并取模得到;
            map.set(this, value);
        } else {
            // 否則,就需要?jiǎng)?chuàng)建新的 ThreadLocalMap 對(duì)象,
            // 然后再將其存儲(chǔ)到當(dāng)前線程的 
            // ThreadLocalMap.ThreadLocalMapHolder.threadLocalMap 中。
            // 這里調(diào)用的是 createMap(t, value) 方法,其實(shí)現(xiàn)在類的另一個(gè)方法中。
            createMap(t, value);
        }
        if (this instanceof TerminatingThreadLocal) {
            // 如果該 ThreadLocal 對(duì)象是 TerminatingThreadLocal 的實(shí)例,
            // 那么就需要調(diào)用 TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this) 方法進(jìn)行注冊(cè)
            TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
        }
        return value;
    }

3.createMap方法

用于為當(dāng)前線程創(chuàng)建 ThreadLocalMap 對(duì)象,并將該對(duì)象存儲(chǔ)到當(dāng)前線程的 threadLocals 屬性中

    /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    // 接受兩個(gè)參數(shù):當(dāng)前線程 t 和 ThreadLocal 對(duì)象的初始值 firstValue 
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

將新創(chuàng)建的 ThreadLocalMap 對(duì)象作為參數(shù)傳入 ThreadLocalMap 的構(gòu)造方法,并將得到的結(jié)果存儲(chǔ)到當(dāng)前線程的 threadLocals 屬性中。需要注意的是,線程的 threadLocals 屬性是一個(gè)
ThreadLocal.ThreadLocalMap 類型的變量,表示線程的本地變量表。如果當(dāng)前線程沒(méi)有 threadLocals 屬性,則會(huì)新建一個(gè)。如果當(dāng)前線程已經(jīng)有了 threadLocals 屬性,則直接使用新建的 ThreadLocalMap 替換原來(lái)的對(duì)象即可。

需要強(qiáng)調(diào)的是,這里的 createMap() 方法是 ThreadLocal 類的一個(gè) protected 方法,因此只能在 ThreadLocal 類及其子類中被調(diào)用。同時(shí),在 InheritableThreadLocal 類中還有一個(gè)覆蓋了該方法的版本,用于處理可以被子線程繼承的線程本地變量。

4.remove方法

    /**
     * Removes the current thread's value for this thread-local
     * variable.  If this thread-local variable is subsequently
     * {@linkplain #get read} by the current thread, its value will be
     * reinitialized by invoking its {@link #initialValue} method,
     * unless its value is {@linkplain #set set} by the current thread
     * in the interim.  This may result in multiple invocations of the
     * {@code initialValue} method in the current thread.
     *
     * @since 1.5
     */
     public void remove() {
         // 通過(guò) Thread.currentThread() 方法獲取當(dāng)前線程對(duì)象 t
         // 通過(guò) getMap(t) 方法獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象 m
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null) {
             // 如果 m 不為 null,就調(diào)用 m.remove(this) 方法將該 ThreadLocal 從 m 中刪除
             m.remove(this);
         }
     }

注意,remove() 方法只會(huì)刪除當(dāng)前線程的 ThreadLocalMap 中與該 ThreadLocal 對(duì)象對(duì)應(yīng)的 entry,不會(huì)影響其他線程的值。

三、ThreadLocalMap的Entry為什么是弱引用?

ThreadLocal的內(nèi)部類ThreadLocalMap源碼可以看到,它的Entry繼承WeakReference,是一個(gè)弱引用的類

   /**
     * ThreadLocalMap is a customized hash map suitable only for
     * maintaining thread local values. No operations are exported
     * outside of the ThreadLocal class. The class is package private to
     * allow declaration of fields in class Thread.  To help deal with
     * very large and long-lived usages, the hash table entries use
     * WeakReferences for keys. However, since reference queues are not
     * used, stale entries are guaranteed to be removed only when
     * the table starts running out of space.
     */
    static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
        .........
    }

其實(shí)使用弱引用是為了避免內(nèi)存泄漏問(wèn)題。

ThreadLocalMap 中的 key 為 ThreadLocal 對(duì)象,value 為線程本地變量對(duì)應(yīng)的副本。當(dāng)一個(gè)線程結(jié)束時(shí),如果不顯式地清理 ThreadLocalMap 中該線程對(duì)應(yīng)的 Entry 對(duì)象,那么這些 Entry 對(duì)象及其對(duì)應(yīng)的 value 副本會(huì)一直存在于內(nèi)存中,就會(huì)導(dǎo)致內(nèi)存泄漏問(wèn)題。

如果 ThreadLocalMap 中使用的是強(qiáng)引用,那么即使線程已經(jīng)結(jié)束,由于這些 Entry 對(duì)象仍然被 ThreadLocalMap 引用著,垃圾回收器也無(wú)法將這些 Entry 對(duì)象回收掉,從而導(dǎo)致內(nèi)存泄漏。

因此,為了避免這種情況,ThreadLocalMap 中使用了 WeakReference 來(lái)引用 ThreadLocal 對(duì)象。WeakReference 對(duì)象具有弱引用特性,當(dāng) ThreadLocal 變量沒(méi)有被其他對(duì)象引用時(shí),就可以被垃圾回收器回收,同時(shí) ThreadLocalMap 中對(duì)應(yīng)的 Entry 對(duì)象也會(huì)被自動(dòng)刪除,從而避免了內(nèi)存泄漏問(wèn)題。

特別需要注意的是,雖然 ThreadLocalMap 中的 Entry 對(duì)象是弱引用,但是 value 部分卻是強(qiáng)引用,因此當(dāng) ThreadLocal 變量被垃圾回收器回收后,對(duì)應(yīng)的 value 副本仍然會(huì)一直存在于內(nèi)存中,需要開發(fā)者手動(dòng)清理。因此,在使用 ThreadLocal 時(shí),需要注意在不需要使用 ThreadLocal 變量時(shí),及時(shí)調(diào)用 remove() 方法進(jìn)行清理。

四、代碼示例和解釋

下面是一個(gè)簡(jiǎn)單的ThreadLocal使用示例:

public class ThreadLocalTest {
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int num = threadLocal.get();
                    System.out.println(Thread.currentThread().getName() + " get num=" + num);
                    threadLocal.set(num + 1);
                    System.out.println(Thread.currentThread().getName() + " set num=" + (num + 1));
                    System.out.println(Thread.currentThread().getName() + " get num=" + threadLocal.get());
                }
            }).start();
        }
    }
}

上面的代碼中,我們使用了一個(gè)靜態(tài)變量threadLocal,它是一個(gè)ThreadLocal對(duì)象。我們?cè)趍ain方法中創(chuàng)建了5個(gè)線程,每個(gè)線程中都調(diào)用了threadLocal的get()方法來(lái)獲取變量值,并輸出線程名和變量值。然后,調(diào)用了threadLocal的set()方法來(lái)修改變量值,并輸出線程名和修改后的變量值。最后再次調(diào)用get()方法來(lái)獲取變量值,并輸出線程名和變量值。

Thread-1 get num=0
Thread-1 set num=1
Thread-1 get num=1
Thread-4 get num=0
Thread-4 set num=1
Thread-4 get num=1
Thread-3 get num=0
Thread-3 set num=1
Thread-3 get num=1
Thread-2 get num=0
Thread-2 set num=1
Thread-2 get num=1
Thread-0 get num=0
Thread-0 set num=1
Thread-0 get num=1

我們可以看到,雖然這個(gè)變量是static的,但是每個(gè)線程都有自己的變量副本,它們互不干擾。這就避免了線程安全問(wèn)題的出現(xiàn)。

五、注意事項(xiàng)

在使用ThreadLocal時(shí),需要注意以下幾點(diǎn):

1.內(nèi)存泄漏問(wèn)題:ThreadLocal 中的變量可以在程序執(zhí)行結(jié)束后一直存在于內(nèi)存中,如果不及時(shí)清理,會(huì)導(dǎo)致內(nèi)存泄漏問(wèn)題。因此,在使用完 ThreadLocal 變量之后,需要手動(dòng)調(diào)用 remove() 方法,將變量和當(dāng)前線程綁定的副本從 ThreadLocalMap 中移除,并釋放相關(guān)內(nèi)存空間。

2.資源占用問(wèn)題:ThreadLocal 對(duì)象本身也是一個(gè)對(duì)象,使用過(guò)多的 ThreadLocal 對(duì)象會(huì)占用大量的內(nèi)存空間。因此,應(yīng)該根據(jù)實(shí)際情況,合理地使用和管理 ThreadLocal 對(duì)象,避免過(guò)度使用。

3.只適用于線程內(nèi)部傳遞數(shù)據(jù):ThreadLocal 對(duì)象只能在同一個(gè)線程內(nèi)部傳遞數(shù)據(jù),無(wú)法在多個(gè)線程之間直接傳遞數(shù)據(jù)。

分享到:
標(biāo)簽:Java
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定