JavaSE|ThreadLocal詳解

概念

ThreadLocal用於提供線程局部變量,在多線程環境可以保證各個線程裏的變量獨立於其他線程裏的變量。也就是說ThreadLocal 可以爲每個線程創建一個【單獨的變量副本】,相當於線程的 private static 類型變量。

ThreadLocal 的作用和同步機制有些相反:同步機制是爲了保證多線程環境下數據的一致性;而 ThreadLocal 是保證了多線程環境下數據的獨立性。

範例:ThreadLocal的簡單使用


public class test {
    private static String commStr;
    private static ThreadLocal<String> threadStr=new ThreadLocal<>();
    public static void main(String[] args){
        
        threadStr.set("Thread-main設置的值");

        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                threadStr.set(Thread.currentThread().getName()+"設置的值");
                System.out.println(Thread.currentThread().getName()+"輸出ThreadLocal的值:"+threadStr.get());
            }
        });
        thread.setName("Thread-user");
        thread.start();


            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        System.out.println(Thread.currentThread().getName()+"輸出ThreadLocal的值:"+threadStr.get());

    }
}


在這裏插入圖片描述

內部實現

ThreadLocal 是怎樣保證其值在各個線程中是獨立的呢?

set(T value)方法

在這裏插入圖片描述

首先獲取當前線程對象,然後獲取到當前線程的ThreadLocalMap,如果ThreadLocalMap不爲null,則將value保存到ThreadLocalMap中,並用當前ThreadLocal 作爲 key;否則創建一個ThreadLocalMap 並給到當前線程,然後保存 value。

ThreadLocalMap 相當於一個 HashMap,是真正保存值的地方。

get()方法

在這裏插入圖片描述

在 get() 方法中也會獲取到當前線程的 ThreadLocalMap,如果 ThreadLocalMap 不爲 null,則把獲取 key 爲當前 ThreadLocal 的值;否則調用 setInitialValue() 方法返回初始值,並保存到新創建的 ThreadLocalMap 中。

remove()方法

在這裏插入圖片描述

ThreadLocalMap

在 set,get,initialValue 和 remove 方法中都會獲取到當前線程,然後通過當前線程獲取到 ThreadLocalMap,如果
ThreadLocalMap 爲 null,則會創建一個 ThreadLocalMap,並給到當前線程。

在使用 ThreadLocal 類型變量進行相關操作時,都會通過當前線程獲取到 ThreadLocalMap 來完成操作。每個線程的
ThreadLocalMap 是 屬 於 線 程 自 己 的 , ThreadLocalMap 中 維 護 的 值 也 是 屬 於 線 程 自 己 的 。 這 就 保 證 了
ThreadLocal 類型的變量在每個線程中是獨立的,在多線程環境下不會相互影響。

構造方法

在這裏插入圖片描述

存儲結構

在這裏插入圖片描述

存儲對象Entry

在這裏插入圖片描述

內存泄露

在 ThreadLocalMap 的 set(),get() 和 remove() 方法中,都有清除無效 Entry 的操作,這樣做是爲了降低內存泄漏發
生的可能。

Entry 中的 key 使用了弱引用的方式,這樣做是爲了降低內存泄漏發生的概率,但不能完全避免內存泄漏。

假設 Entry 的 key 沒有使用弱引用的方式,而是使用了強引用:由於 ThreadLocalMap 的生命週期和當前線程一樣長,那麼當引用 ThreadLocal 的對象被回收後,由於ThreadLocalMap 還持有 ThreadLocal 和對應 value 的強引用,ThreadLocal 和對應的 value 是不會被回收的,這就導致了內存泄漏。所以 Entry 以弱引用的方式避免了 ThreadLocal
沒有被回收而導致的內存泄漏,但是此時 value 仍然是無法回收的,依然會導致內存泄漏。

ThreadLocalMap 已經考慮到這種情況,並且有一些防護措施:在調用 ThreadLocal 的 get(),set() 和 remove() 的時候都會清除當前線程 ThreadLocalMap 中所有 key 爲 null 的value。這樣可以降低內存泄漏發生的概率。所以我們在使用 ThreadLocal 的時候,每次用完 ThreadLocal 都調用 remove() 方法,清除數據,防止內存泄漏。

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