91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java線程本地變量ThreadLocal該怎么理解

發布時間:2021-12-17 15:53:58 來源:億速云 閱讀:159 作者:柒染 欄目:編程語言

這篇文章給大家介紹java線程本地變量ThreadLocal該怎么理解,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

介紹

ThreadLocal作為JDK1.2以來的一個java.lang包下的一個類,在面試和工程中都非常重要,這個類的主要目的是提供線程本地的變量,所以也有很多地方把這個類叫做線程本地變量

從字面理解,這個類為每個線程都創建了一個本地變量,實際上是ThreadLocal為變量在每個線程中都創建了一個副本,使得每個線程都可以訪問自己內部的副本變量

通常提到多線程,都會考慮變量同步的問題,但是ThreadLocal并不是為了解決多線程共享變量同步的問題,而是為了讓每個線程的變量不互相影響,相當于線程之間操縱的都是變量的副本,自然就不用考慮多線程競爭的問題,也自然沒有性能損耗

使用方式

先來看常用的這幾個方法

public T get() { }public void set(T value) { }public void remove() { }protected T initialValue() { }

顯而易見,get()方法獲取線程擁有的副本值,set()方法進行設值,remove()方法移除,initialValue()進行變量初始化,我們先來看下面這個實例,同時體會一下應用場景

public class Demo {public static ThreadLocal<Integer> threadLocal = null;public static void main(String[] args) {threadLocal = new ThreadLocal<Integer>() {

/*** 通過重寫該方法來初始化ThreadLocal的值*/@Overrideprotected Integer initialValue() {return 10;}};MyThread t1 = new MyThread(20);

MyThread t2 = new MyThread(30);t1.start();

// 這里為了描述清晰,省略了try-catch語句塊t1.join();t2.start();}}

在上述方法中,我們定義并初始化一個ThreadLocal類為10(通過重寫initialValue()方法實現),然后開啟了兩個線程,同時我們這里讓t2線程等待t1線程執行完再執行

MyThread類詳細信息如下

class MyThread extends Thread {private int val = 0;MyThread(int val) {this.val = val;}@Overridepublic void run() {System.out.println(Thread.currentThread() + "-BEFORE-" + Demo.threadLocal.get());Demo.threadLocal.set(val);System.out.println(Thread.currentThread() + "-AFTER-" + Demo.threadLocal.get());}}

我們通過調用ThreadLocal對象的get()方法來獲取當前的值,然后通過set()方法設置一個新值(每個線程我們設置不同的值),然后再通過get()方法來獲取設置后的值

運行結果如下

重點是圖中標注的t2線程變量的初始值,雖然我們在t1線程中修改了變量的值,但是在t2線程中變量值并沒有被改變,這樣就實現了每個線程獨有的變量

同時,如果一個ThreadLocal對象要在很多地方進行復用時,需要在使用前通過調用**remove()**方法來將本地變量恢復到默認值

也許有人會問了,我們給每個線程定義自己的私有變量不是也可以實現同樣的操作嗎,理論上當然是可行的,但是ThreadLocal遠比私有變量的形式方便,不僅可以在線程外部進行統一的初始化,而且避免在線程內部額外設置變量

原理

點進ThreadLocal的源碼中,發現并沒有存儲變量的字段值,那看來ThreadLocal并不負責保存變量,我們只能從方法下手

先看initial()方法,畢竟我們的變量默認初始值就是在這個方法中設置,如下

protected T initialValue() {return null;}

我們在每次創建ThreadLocal都要重寫這個方法,那么這個方法到底在哪調用呢,我們點進get()方法源碼中,如下

public T get() {

// 獲取當前線程Thread t = Thread.currentThread();

// 獲取當前線程的mapThreadLocalMap map = getMap(t);

if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);

if (e != null) {T result = (T)e.value;return result;}}

// 如果map為空則進行創建return setInitialValue();}

有點眉頭了,我們發現這里獲取了一個ThreadLocalMap對象,所以會想到有可能是通過讓線程與變量作一個KV表,來實現每個線程擁有自己獨有的變量

我們點進getMap(t)方法中,發現返回了線程t的一個threadLocals屬性,這是Thread類的一個字段:

ThreadLocal.ThreadLocalMap threadLocals = null;

這是一個由ThreadLocal類維護的屬性,Thread的任何方法都沒有對這個字段進行修改操作,而這個ThreadLocalMap本身又是ThreadLocal的一個內部類,可以把它理解成一個Map(雖然這個類沒有繼承Map接口)

同時要注意,在ThreadLocalMap對象中的Entry對象(鍵值對),繼承了一個ThreadLocaMap的弱引用,如下

static class Entry extends WeakReference<ThreadLocal<?>> {

/** The value associated with this ThreadLocal. 

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

也就是說,當ThreadLocal被置為空時,Entry中的Key則會在下一次YGC中被回收

我們還是沒有看到initialValue()方法,別急,點進setInitialValue()方法,也就是如果在get()方法中檢測到map為空時調用的方法,如下

private T setInitialValue() {

// 我們設定的初始值T value = initialValue();

// 當前線程Thread t = Thread.currentThread();

// 再檢查一次是否為空ThreadLocalMap map = getMap(t);

if (map != null)map.set(this, value);

elsecreateMap(t, value);

return value;}

現在基本操作我們都清楚了,set()方法和initialValue()幾乎完全一致,remove()方法則是普通地移除了一個KV鍵值對(K為當前線程),這里均不再列出,如果感興趣可以自行查看

注意事項

1.臟數據

從上面的分析可以看出,ThreadLocal是和Thread綁定的,每一個Thread對應一個value,如果沒有在使用結束后調用remove()方法,就會在下一次重用時讀到臟數據(針對同一個線程而言),尤其是使用線程池的場景(線程池中的線程經常會復用)

2.內存泄露

一般在使用時都會將ThreadLocal設置為靜態字段,這時候當線程執行完成后,KV中的V是不會自動回收的,所以要在使用完后及時調用remove()方法清理

關于java線程本地變量ThreadLocal該怎么理解就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

临江市| 伊通| 德昌县| 时尚| 玛沁县| 大渡口区| 武威市| 定陶县| 苏尼特右旗| 卫辉市| 铜山县| 克拉玛依市| 保靖县| 当涂县| 南郑县| 玛多县| 高平市| 和林格尔县| 沾益县| 绥德县| 吉木乃县| 通河县| 东莞市| 江油市| 柞水县| 浏阳市| 合江县| 武夷山市| 东平县| 嵊泗县| 班玛县| 林州市| 阿尔山市| 托克逊县| 禄丰县| 平度市| 大化| 云南省| 新巴尔虎左旗| 海伦市| 永州市|