ThreadLocal不懂的就過來看看吧

既然寫了博客我就寫的詳細一些,儘量易懂一些(反正我也操作了,哈哈),以後慢慢的就不會了呦

來上才藝了哈:學習、認識三部曲:

1. ThreadLocal是什麼
2. ThreadLocal的應用
3. ThreadLocal怎麼實現的
4. ThreadLocal有沒有問題(內存泄漏)
5. 融會貫通

那我們開始吧

ThreadLocal是什麼

當程序每新啓動一個線程(web應用上包括用戶訪問),ThreadLocal會依據你當前的線程給你保存一些信息,僅僅是和你當前線程相關。

ThreadLocal的應用

既然可以攜帶和你當前線程有關的信息,那是不是我可以把每個訪問線程都放到裏面,就方便存取了呢,哪些應用可以實現(之前接觸的有放入用戶信息的),做的最多的可能就是去操作數據,裏面放的都connection信息,這樣操作就相當於每個線程只建立一個連接。給你個示例代碼自己先看看

public class DBUtil {
    // 數據庫配置
    private static final String driver = "com.mysql.jdbc.Driver";
    private static final String url = "jdbc:mysql://localhost:3306/demo";
    private static final String username = "root";
    private static final String password = "root";

    // 定義一個用於放置數據庫連接的局部線程變量(使每個線程都擁有自己的連接)
    private static ThreadLocal<Connection> connContainer = new ThreadLocal<Connection>();

    // 獲取連接
    public static Connection getConnection() {
        Connection conn = connContainer.get();
        try {
            if (conn == null) {
                Class.forName(driver);
                conn = DriverManager.getConnection(url, username, password);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            connContainer.set(conn);
        }
        return conn;
    }

    // 關閉連接
    public static void closeConnection() {
        Connection conn = connContainer.get();
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            connContainer.remove();
        }
    }
}

ThreadLocal怎麼實現的

既然已經知道獲取的是當前線程相關的那就解析一下這個過程。先上類圖:
在這裏插入圖片描述
應該先看 Thread 裏面:

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

threadLocals 是一個map 沒錯就是它,它保存的是你的當前線程和你對應的value,對應的
ThreadLocalMap 是ThreadLocal的內部類。這個就說明了,我把它交給了ThreadLocal去管理。
那現在就得看它的初始化了,一開始它做了什麼:

   /**
     * 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() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
     /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

當然了有些會重寫它的初始化,在獲取的時候,這樣就可以直接賦值了。
看到了吧 threadLocals 的key 就是 當前的ThreadLocal。不行再看下set 和 get 哈

   /**
     * 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);
    }

我已經盡力了。
簡單的說就是:
Thread.threadLocals(map) ------>ThreadLocal.ThreadLocalMap(ThreadLocal爲鍵,注意ThreadLocalMap是個靜態class)------>set、get 調用初始化賦值

ThreadLocal有沒有問題(內存泄漏)

Thread---->ThreadLocal.ThreadLocalMap
其實還是map中的內存泄漏問題。map中把theadlocal 變成null,GC之後在map中存儲的弱引用對象的hashcode來作爲key,那麼它就被回收了,但是它的value一直就被放在堆裏面,並且被當前的線程threadLocals引用,因爲在線程池裏的他可能一直都不釋放,導致內存泄漏了。 所以導致內存泄漏的情況需要滿足:

  1. 觸發了垃圾回收
  2. 沒有調用get、set、remove因爲如果調用這個threadlocal 會發現鍵爲空了,會重新賦值
  3. 線程一直運行
    所以解決方案:
    最簡單有效的方法是使用後將其移除 記得用remove

過程大家可以參考這個我覺得還沒他寫的好
ThreadLocal理解及應用
我們來分析一下這個mat圖:
這個視圖呢,是分析存活的最大空間佔用的,在每一列的頭部可以正則搜索,我們搜索
在這裏插入圖片描述
這個是分析對象實例個數和佔用的
在這裏插入圖片描述
這個工具還可以分析多個dump文件來進行對比分析
JVM故障分析及性能優化
然後搜索發現沒有key的引用,只有value的空間佔用很大。
驗證了內存泄漏的問題

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