欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

重新認識Java中的ThreadLocal

 更新時間:2021年05月31日 08:36:37   作者:Nicksxs''''s  
ThreadLocal是JDK包提供的,它提供線程本地變量,如果創(chuàng)建一個ThreadLocal變量,那么訪問這個變量的每個線程都會有這個變量的一個副本,在實際多線程操作的時候,操作的是自己本地內(nèi)存中的變量,從而規(guī)避了線程安全問題

說來也慚愧,這個 ThreadLocal 其實一直都是一知半解,而且看了一下之后還發(fā)現(xiàn)記錯了,所以還是記錄下
原先記憶里的都是反過來,一個 ThreadLocal 是里面按照 thread 作為 key,存儲線程內(nèi)容的,真的是半解都米有,完全是錯的,這樣就得用 concurrentHashMap 這種去存儲并且要鎖定線程了,然后內(nèi)容也只能存一個了,想想簡直智障

究竟是啥結(jié)構(gòu)

比如我們在代碼中 new 一個 ThreadLocal,

public static void main(String[] args) {
        ThreadLocal<Man> tl = new ThreadLocal<>();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(tl.get());
        }).start();
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tl.set(new Man());
        }).start();
    }

    static class Man {
        String name = "nick";
    }

這里構(gòu)造了兩個線程,一個先往里設(shè)值,一個后從里取,運行看下結(jié)果,

知道這個用法的話肯定知道是取不到值的,只是具體的原理原來搞錯了,我們來看下設(shè)值 set 方法

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

寫博客這會我才明白我原來咋會錯得這么離譜,看到第一行代碼 t 就是當前線程,然后第二行就是用這個線程去getMap,然后我是把這個當成從 map 里取值了,其實這里是

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

獲取 t 的 threadLocals 成員變量,那這個 threadLocals 又是啥呢

它其實是線程 Thread 中的一個類型是java.lang.ThreadLocal.ThreadLocalMap的成員變量
這是 ThreadLocal 的一個靜態(tài)成員變量

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

全部代碼有點長,只截取了一小部分,然后我們再回頭來分析前面說的 set 過程,再 copy 下代碼

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

獲取到 map 以后呢,如果 map 不為空,就往 map 里 set,這里注意 key 是啥,其實是當前這個 ThreadLocal,這里就比較明白了究竟是啥結(jié)構(gòu),每個線程都會維護自身的 ThreadLocalMap,它是線程的一個成員變量,當創(chuàng)建 ThreadLocal 的時候,進行設(shè)值的時候其實是往這個 map 里以 ThreadLocal 作為 key,往里設(shè) value。

內(nèi)存泄漏是什么鬼

這里又要看下前面的 ThreadLocalMap 結(jié)構(gòu)了,類似 HashMap,它有個 Entry 結(jié)構(gòu),在設(shè)置的時候會先包裝成一個 Entry

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

這里其實比較重要的就是前面的 Entry 的構(gòu)造方法,Entry 是個 WeakReference 的子類,然后在構(gòu)造方法里可以看到 key 會被包裝成一個弱引用,這里為什么使用弱引用,其實是方便這個 key 被回收,如果前面的 ThreadLocal tl實例被設(shè)置成 null 了,如果這里是直接的強引用的話,就只能等到線程整個回收了,但是其實是弱引用也會有問題,主要是因為這個 value,如果在 ThreadLocal tl 被設(shè)置成 null 了,那么其實這個 value 就會沒法被訪問到,所以最好的操作還是在使用完了就 remove 掉

以上就是詳解Java中的ThreadLocal的詳細內(nèi)容,更多關(guān)于Java ThreadLocal的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論