亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享
原創

ThreadLocal

2024-03-28 09:29:34
3
0

一、ThreadLocal

ThreadLocal是本地線程變量,其中填充的的是當前線程的變量,該變量對其他線程而言是封閉且隔離的,ThreadLocal為變量在每個線程中創建了一個副本,這樣每個線程都可以訪問自己內部的副本變量。

它與普通變量的區別在于,每個使用該變量的線程都會初始化一個完全獨立的實例副本,通過統一的操作方式,實現了不同線程使用不通的變量,使用起來比較方便

Thread_1 --------> var_1
Thread_2 --------> var_2
Thread_3 --------> var_3
...

二、ThreadLocal怎么用

public class Main {
    public static void main(String[] args) {
        ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
        IntStream.range(0, 10).forEach(i -> {
            new Thread(() -> {
                threadLocal.set(i);
                // 業務邏輯...
                System.out.println(Thread.currentThread().getName() + "threadLocal var: " + threadLocal.get());
            }).start();
        });
    }
}

/**
 * Thread-0threadLocal var: 0
 * Thread-4threadLocal var: 4
 * Thread-3threadLocal var: 3
 * Thread-2threadLocal var: 2
 * Thread-1threadLocal var: 1
 */

可以看到在不同的線程中通過訪問同一變量threadLocal,而獲取到的是不同的值。

在一個應用系統中,一般是通過創建一個線程去負責處理業務邏輯,而業務邏輯的處理過程,通常需要跨越多個方法,多個方法之間可能需要相同的參數,我們稱為context上下文信息,如果方法之間來回傳遞這個context是比較麻煩的,而ThreadLocal很好地解決了這個問題。

從實際應用的角度,ThreadLocal可以:

  1. 在進行對象跨層傳遞的時候,使用ThreadLocal可以避免多次傳遞,打破層次間的約束。
  2. 線程間數據隔離
  3. 進行事務操作,用于存儲線程事務信息。
  4. 數據庫連接,Session會話管理。

三、ThreadLocal的實現原理

Thread類中有兩個變量:

  1. threadLocals
  2. inheritableThreadLocals

二者的類型都是ThreadLocal的內部類ThreadLocalMap類型,其類似HashMap,默認情況下二者都是null。當線程第一次調用ThreadLocal的get或set方法時會創建它們。

/---------------------------\			/-----------------------\
|				  			|			|						|
|		 	Thread	  		|			|	ThreadLocalMap		|
|							|			|						|
|1. threadLocals            |			|	Entry				|
|2. inheritableThreadLocals |			|		key | value		|
|							|			|		tl  |  var		|
|							|			|						|
\---------------------------/			\-----------------------/

ThreadLocal的set()方法:

public void set(T value) {
    // 獲取當前線程,稱為調用者線程
    Thread t = Thread.currentThread();
    // 以調用者線程作為key值,去查找對應的線程變量,找到對應的map
    ThreadLocalMap map = getMap(t); 
    // 如果map不為null,就直接添加本地變量,key為當前定義的ThreadLocal變量的this引用,值為添加的本地變量值
    if (map != null)
        map.set(this, value);
    // 如果map為null,說明首次添加,需要首先創建出對應的map
    else
        createMap(t, value);
}

ThreadLocalMap getMap(Thread t) {
    // 獲取線程自己的變量threadLocals,并綁定到當前調用線程的成員變量threadLocals上
    return t.threadLocals; 
}

void createMap(Thread t, T firstValue) {
    // createMap方法不僅創建了threadLocals,同時也將要添加的本地變量值添加到了threadLocals中。
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

get()方法:

public T get() {
    // 獲取調用者線程
    Thread t = Thread.currentThread();
    // 獲取當前線程的threadLocals變量
    ThreadLocalMap map = getMap(t);
    // 如果threadLocals變量不為null,就可以在map中查找到本地變量的值
    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() {
    //protected T initialValue() {return null;}
    T value = initialValue();
    // 獲取調用者線程
    Thread t = Thread.currentThread();
    // 以當前線程作為key值,去查找對應的線程變量,找到對應的map
    ThreadLocalMap map = getMap(t);
    // 如果map不為null,就直接添加本地變量,key為當前線程,值為添加的本地變量值
    if (map != null)
        map.set(this, value);
    // 如果map為null,說明首次添加,需要首先創建出對應的map
    else
        createMap(t, value);
    return value;
}

所以在不同的線程中通過get()方法獲取threadLocal變量,本質上是從當前thread的threaLocals存的變量。

四、避免內存泄漏

在第三節介紹了每個Thread都維護了一個ThreadLocalMap,key為ThreadLocal實例,為弱引用,value為線程變量的副本,他們的引用關系:

										-------------------------------------Heap-----------------------\
/-----------Stack-----------\			|				------------\								 	|
|				  			|			|				|			|									|
|							|			|				v			|									|
|	ThreadLocalRef	--------------------|-----> ThreadLocal  		|		/-------------------\		|
|							|			|							|		|	ThreadLocalMap	|		|
|	CurrentTreadRef	--------------------|----->	CurrentThread ------|----->	|	Entry			|		|
|							|			|							|		|		key | value	|		|
|							|			|							|		|		tl  |  var  |		|
\---------------------------/			|							|		|		 |		|	|		|
										|							|		\--------|------|---/		|
										|							\----------------/		|			|
										\---------------------------------------------------------------/
																							|	
																					   		v
																					    線程變量val

從它們的引用關系可以看出來,threadLocalMap中的key為ThreadLocal的弱引用,當ThreadLocal在其他地方不存在強引用時,key(ThreadLocal)勢必會被GC回收,這樣key變成了null。而value還存在著強引用不會被GC回收,只有當當前進程結束這個強引用才會斷開,但如果線程遲遲不結束,這個強引用就會一直存在,value就會一直存在內存中,造成內存泄漏。

因此如果threadlocal不再使用時,通過remove()方法刪除,避免內存泄漏的情況發生。

0條評論
0 / 1000
cactusii
15文章數
0粉絲數
cactusii
15 文章 | 0 粉絲
原創

ThreadLocal

2024-03-28 09:29:34
3
0

一、ThreadLocal

ThreadLocal是本地線程變量,其中填充的的是當前線程的變量,該變量對其他線程而言是封閉且隔離的,ThreadLocal為變量在每個線程中創建了一個副本,這樣每個線程都可以訪問自己內部的副本變量。

它與普通變量的區別在于,每個使用該變量的線程都會初始化一個完全獨立的實例副本,通過統一的操作方式,實現了不同線程使用不通的變量,使用起來比較方便

Thread_1 --------> var_1
Thread_2 --------> var_2
Thread_3 --------> var_3
...

二、ThreadLocal怎么用

public class Main {
    public static void main(String[] args) {
        ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
        IntStream.range(0, 10).forEach(i -> {
            new Thread(() -> {
                threadLocal.set(i);
                // 業務邏輯...
                System.out.println(Thread.currentThread().getName() + "threadLocal var: " + threadLocal.get());
            }).start();
        });
    }
}

/**
 * Thread-0threadLocal var: 0
 * Thread-4threadLocal var: 4
 * Thread-3threadLocal var: 3
 * Thread-2threadLocal var: 2
 * Thread-1threadLocal var: 1
 */

可以看到在不同的線程中通過訪問同一變量threadLocal,而獲取到的是不同的值。

在一個應用系統中,一般是通過創建一個線程去負責處理業務邏輯,而業務邏輯的處理過程,通常需要跨越多個方法,多個方法之間可能需要相同的參數,我們稱為context上下文信息,如果方法之間來回傳遞這個context是比較麻煩的,而ThreadLocal很好地解決了這個問題。

從實際應用的角度,ThreadLocal可以:

  1. 在進行對象跨層傳遞的時候,使用ThreadLocal可以避免多次傳遞,打破層次間的約束。
  2. 線程間數據隔離
  3. 進行事務操作,用于存儲線程事務信息。
  4. 數據庫連接,Session會話管理。

三、ThreadLocal的實現原理

Thread類中有兩個變量:

  1. threadLocals
  2. inheritableThreadLocals

二者的類型都是ThreadLocal的內部類ThreadLocalMap類型,其類似HashMap,默認情況下二者都是null。當線程第一次調用ThreadLocal的get或set方法時會創建它們。

/---------------------------\			/-----------------------\
|				  			|			|						|
|		 	Thread	  		|			|	ThreadLocalMap		|
|							|			|						|
|1. threadLocals            |			|	Entry				|
|2. inheritableThreadLocals |			|		key | value		|
|							|			|		tl  |  var		|
|							|			|						|
\---------------------------/			\-----------------------/

ThreadLocal的set()方法:

public void set(T value) {
    // 獲取當前線程,稱為調用者線程
    Thread t = Thread.currentThread();
    // 以調用者線程作為key值,去查找對應的線程變量,找到對應的map
    ThreadLocalMap map = getMap(t); 
    // 如果map不為null,就直接添加本地變量,key為當前定義的ThreadLocal變量的this引用,值為添加的本地變量值
    if (map != null)
        map.set(this, value);
    // 如果map為null,說明首次添加,需要首先創建出對應的map
    else
        createMap(t, value);
}

ThreadLocalMap getMap(Thread t) {
    // 獲取線程自己的變量threadLocals,并綁定到當前調用線程的成員變量threadLocals上
    return t.threadLocals; 
}

void createMap(Thread t, T firstValue) {
    // createMap方法不僅創建了threadLocals,同時也將要添加的本地變量值添加到了threadLocals中。
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

get()方法:

public T get() {
    // 獲取調用者線程
    Thread t = Thread.currentThread();
    // 獲取當前線程的threadLocals變量
    ThreadLocalMap map = getMap(t);
    // 如果threadLocals變量不為null,就可以在map中查找到本地變量的值
    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() {
    //protected T initialValue() {return null;}
    T value = initialValue();
    // 獲取調用者線程
    Thread t = Thread.currentThread();
    // 以當前線程作為key值,去查找對應的線程變量,找到對應的map
    ThreadLocalMap map = getMap(t);
    // 如果map不為null,就直接添加本地變量,key為當前線程,值為添加的本地變量值
    if (map != null)
        map.set(this, value);
    // 如果map為null,說明首次添加,需要首先創建出對應的map
    else
        createMap(t, value);
    return value;
}

所以在不同的線程中通過get()方法獲取threadLocal變量,本質上是從當前thread的threaLocals存的變量。

四、避免內存泄漏

在第三節介紹了每個Thread都維護了一個ThreadLocalMap,key為ThreadLocal實例,為弱引用,value為線程變量的副本,他們的引用關系:

										-------------------------------------Heap-----------------------\
/-----------Stack-----------\			|				------------\								 	|
|				  			|			|				|			|									|
|							|			|				v			|									|
|	ThreadLocalRef	--------------------|-----> ThreadLocal  		|		/-------------------\		|
|							|			|							|		|	ThreadLocalMap	|		|
|	CurrentTreadRef	--------------------|----->	CurrentThread ------|----->	|	Entry			|		|
|							|			|							|		|		key | value	|		|
|							|			|							|		|		tl  |  var  |		|
\---------------------------/			|							|		|		 |		|	|		|
										|							|		\--------|------|---/		|
										|							\----------------/		|			|
										\---------------------------------------------------------------/
																							|	
																					   		v
																					    線程變量val

從它們的引用關系可以看出來,threadLocalMap中的key為ThreadLocal的弱引用,當ThreadLocal在其他地方不存在強引用時,key(ThreadLocal)勢必會被GC回收,這樣key變成了null。而value還存在著強引用不會被GC回收,只有當當前進程結束這個強引用才會斷開,但如果線程遲遲不結束,這個強引用就會一直存在,value就會一直存在內存中,造成內存泄漏。

因此如果threadlocal不再使用時,通過remove()方法刪除,避免內存泄漏的情況發生。

文章來自個人專欄
文章 | 訂閱
0條評論
0 / 1000
請輸入你的評論
0
0