1. 程式人生 > 實用技巧 >(精華)2020年8月12日 C#基礎知識點 檔案相關操作

(精華)2020年8月12日 C#基礎知識點 檔案相關操作

多個執行緒需要對一個共享變數寫入時容易出現併發問題。
ThreadLocal可以做到:當多個執行緒對一個共享變數進行訪問的時候,實際上訪問的是本執行緒的本地變數。

一、ThreadLocal實現原理

Thread類中有threadlocals變數,threadlocals變數的型別是ThreadLocal.ThreadLocalMap,是在ThreadLocal類中實現的一個類似於Map的結構,ThreadLocalMap結構實際上是一個Entry對,key值是ThreadLocal物件,value值是ThreadLocal物件的值,如下圖所示:

二、ThreadLocal實現細節

1、get實現

    public T get() {
        //獲取當前執行緒
        Thread t = Thread.currentThread();
        //獲取當前執行緒的threadLocals變數
        ThreadLocalMap map = getMap(t);
        //如果當前執行緒的threadLocals變數不為空,則返回對應的value值
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        //如果當前執行緒threadLocals變數為null,則初始化當前執行緒的threadLocals變數
        return setInitialValue();
    }
    private T setInitialValue() {
        //定義value = null
        T value = initialValue();
        //獲取當前執行緒
        Thread t = Thread.currentThread();
        //獲取當前執行緒的threadLocals變數
        ThreadLocalMap map = getMap(t);
        //如果當前執行緒的threadLocals變數不為空,則將當前的threadLocals變數的value值置為null
        if (map != null)
            map.set(this, value);
       //如果當前執行緒的threadLocals變數為空,則初始化建立一個當前執行緒的threadLocals變數,並將value置為null
        else
            createMap(t, value);
        return value;
    }

2、set實現

    public void set(T value) {
        //獲取當前執行緒
        Thread t = Thread.currentThread();
        //獲取當前執行緒的threadLocals變數
        ThreadLocalMap map = getMap(t);
        //如果當前執行緒的threadLocals變數不為空,則將該變數的value值設定為傳入的value值
        if (map != null)
            map.set(this, value);
        //如果當前執行緒的threadLocals變數為空,則初始化建立一個當前執行緒的threadLocals變數,並將該變數的value值設定為傳入的value值
        else
            createMap(t, value);
    }

3、remove實現

     public void remove() {
         //獲取當前執行緒的threadLocals變數
         ThreadLocalMap m = getMap(Thread.currentThread());
         //如果不為空,則刪除當前執行緒中指定ThreadLocal例項的本地變數
         if (m != null)
             m.remove(this);
     }

三、記憶體洩露

我們先看看ThreadLocal態類裡面的成員:

public class ThreadLocal<T>{
      //......

      static class ThreadLocalMap{
            static class Entry extends WeakReference<ThreadLocal<?>>{
                  Object value;
                  
                  Entry(ThreadLocal<?> k, Object v){
                        super(k);
                        value = v;
                  }
            }
            //......
      }
      //......
}

當一個執行緒呼叫ThreadLocal類的set方法設定當前執行緒的本地變數時,當前執行緒的ThreadLocalMap中會有一條記錄,該記錄的key值為ThreadLocal的弱引用,value為set方法設定的值。

如果當前執行緒一直存在,且沒有呼叫ThreadLocal的remove方法,且這時候還有其他地方還有對ThreadLocal的引用,則當前執行緒的ThreadLocalMap變數裡會同時存在對ThreadLocal變數的引用和對value物件的引用,它們不會被釋放,因此會造成記憶體洩露。

由於執行緒的ThreadLocalMap中的key是弱引用,所以當前執行緒的ThreadLocalMap中的ThreadLocal變數的弱引用會在下一次gc時被回收,但是對應的value還是會造成記憶體洩露,這時候ThreadLocalMap中會存在key為null但是value不為null的Entry項。

解決方法:在每次使用完畢後呼叫remove方法。