ThreadLocal原理用法詳解ThreadLocal記憶體洩漏

維護世界和平有我發表於2020-10-29

ThreadLocal

https://github.com/CyC2018/CS-Notes/blob/master/notes/Java%20%E5%B9%B6%E5%8F%91.md#2-%E7%BA%BF%E7%A8%8B%E6%9C%AC%E5%9C%B0%E5%AD%98%E5%82%A8thread-local-storage

ThreadLocal,即執行緒變數

常用的3個方法:set()、get()、remove()。都是執行緒安全的。

一個ThreadLocal在一個執行緒中是共享的,在不同執行緒之間又是隔離的(每個執行緒都只能看到自己執行緒的值)

public class ThreadLocalExample1 {
    public static void main(String[] args) {
        ThreadLocal threadLocal1 = new ThreadLocal();
        ThreadLocal threadLocal2 = new ThreadLocal();
        Thread thread1 = new Thread(() -> {
            threadLocal1.set(1);
            //threadLocal1.set(11);會覆蓋上面的set(1)
            threadLocal2.set(1);
        });
        Thread thread2 = new Thread(() -> {
            threadLocal1.set(2);
            threadLocal2.set(2);
        });
        thread1.start();
        thread2.start();
    }
}

ThreadLocal原理

在這裡插入圖片描述

每個 Thread 都有一個 ThreadLocal.ThreadLocalMap 物件。

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

set

ThreadLocal.ThreadLocalMap threadLocals = null;
...
public void set(T value) {
    Thread t = Thread.currentThread();
    //獲取當前執行緒的ThreadLocalMap;
    ThreadLocalMap map = getMap(t);
    if (map != null)
    //this是呼叫set()方法的localthread物件;
        map.set(this, value);
    else
        createMap(t, value);
}
...
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

set()先獲取當前執行緒的ThreadLocalMap,然後將呼叫set()方法的localthread物件作為key儲存value,

這樣的話,ThreadLocal threadLocal1 = new ThreadLocal(),執行緒a呼叫threadLocal1.set方法時,執行緒a會建立ThreadLocalMap物件,key是threadLocal1;
當執行緒呼叫threadLocal1.get時,執行緒a便會到自己的ThreadLocalMap中,根據key==threadLocal1獲取value,這樣便是執行緒安全的,每個執行緒都有自己的ThreadLocalMap

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,然後根據將呼叫get()方法的localthread物件作為key獲取value;

ThreadLocal記憶體洩漏

記憶體洩露:程式在申請記憶體後,無法釋放已申請的記憶體空間,一次記憶體洩露危害可以忽略,但記憶體洩露堆積後果很嚴重;

ThreadLocalMap的key

static class ThreadLocalMap {

    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

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

在這裡插入圖片描述

如上圖,每一個Thread維護一個ThreadLocalMap,key弱引用,外部的ThreadLocalRef是key的強引用,一旦ThreadLocalRefnull,GC時key就會被回收,但是value還存在強引用,只有CurrentThreadRefnull,value才會被回收;

這些key為null的Entry的value就會一直存在一條強引用鏈

CurrentThreadRef -> CurrentThread -> ThreaLocalMap -> Entry -> value

public class ThreadLocalExample1 {
    public static void main(String[] args) {
        ThreadLocal threadLocal1 = new ThreadLocal();
        Thread thread1 = new Thread(() -> {
        while(true){
            threadLocal1.set(1);
        }
        });
        threadLocal1 = null;
        System.GC();//如果在這裡發生GC,那麼threadLocal1以及thread1的ThreaLocalMap的key會被回收,但是value不會被回收;
        thread1.start();
    }
}

https://blog.csdn.net/puppylpg/article/details/80433271
https://zhuanlan.zhihu.com/p/102571059


相關文章