Java多線程之ThreadLocal的使用
這篇文章帶給大家的內容是關於Java多執行緒之ThreadLocal的使用,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
在多執行緒環境下,存取非執行緒安全性的變數時必須進行執行緒同步,例如使用synchronized
方式存取HashMap
實例。但是同步存取會降低並發性,影響系統效能。這時候就可以用空間換時間,如果我們給每個線程都分配一個獨立的變量,就可以用非同步的方式使用非線程安全的變量,我們稱這種變量為線程局部變量。
顧名思義,線程局部變數是指每個執行緒都有屬於自己獨立的變數副本,不會像普通局部變數一樣可以被其他執行緒存取。 Java
並沒有提供語言層級的線程局部變量,而是在類別庫裡提供了線程局部變數的功能,也就是這次的主角ThreadLocal
類別。
ThreadLocal的使用
#Java8版本的ThreadLocal有上圖所示的4個public方法和一個protected的方法,第一個方法用來傳回初始值,預設是null。第二個靜態方法withInitial(Supplier extends S> supplier)是Java8版本新加入的,後面三個實例方法則非常簡單的。
在Java8之前,使用ThreadLocal時想要設定初始值時需要繼承ThreadLocal類別覆寫protected T initialValue()方法才行,例如:
ThreadLocal<integer> threadLocal = new ThreadLocal<integer>() { @Override protected Integer initialValue() { return 0; } };</integer></integer>
在Java8版本可以使用新加入的靜態方法withInitial(Supplier extends S> supplier),非常方便的設定初始值,例如:
ThreadLocal<integer> threadLocal = ThreadLocal.withInitial(() -> 0); System.out.println(threadLocal.get()); threadLocal.set(16); System.out.println(threadLocal.get()); threadLocal.remove(); System.out.println(threadLocal.get()); // 同一个线程的输出 0 16 0 Process finished with exit code 0</integer>
ThreadLocal的原理
那麼ThreadLocal是怎麼實現線程的局部變數的函數呢?其實ThreadLocal的基本原理並沒有十分複雜。 ThreadLocal在內部定義了一個靜態類別ThreadLocalMap,ThreadLocalMap的鍵為ThreadLocal對象,ThreadLocalMap的值就是ThreadLocal儲存的數值,不過這個ThreadLocalMap是在Thread類別裡維護的。我們來看看ThreadLocal的部分原始碼:
// ThreadLocal的set方法 public void set(T value) { // 获取当前线程对象 Thread t = Thread.currentThread(); // 获取Map ThreadLocalMap map = getMap(t); if (map != null) // 设置值 map.set(this, value); else // 初始化Map createMap(t, value); } // ThreadLocal的createMap方法 void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } // Thread类定义的实例域 /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
可以看出ThreadLocal的核心實作就是ThreadLocalMap的實作了,ThreadLocalMap內部宣告了一個Entry類別來儲存資料:
static class Entry extends WeakReference<threadlocal>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal> k, Object v) { super(k); value = v; } }</threadlocal>
ThreadLocalMap的實作與HashMap的實作有相似的地方,例如同樣是使用陣列儲存資料和自動擴容,不同的是hash演算法與hash碰撞後的處理不一樣。
// ThreadLocalMap的set方法 private void set(ThreadLocal> key, Object value) { Entry[] tab = table; int len = tab.length; // 计算在Entry[]中的索引,每个ThreadLocal对象都有一个hash值threadLocalHashCode,每初始化一个ThreadLocal对象,hash值就增加一个固定的大小0x61c88647 int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal> k = e.get(); // 如果键已存在就更新值 if (k == key) { e.value = value; return; } // 代替无效的键 if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } private static int nextIndex(int i, int len) { return ((i + 1 <p>可以看到ThreadLocalMap把Entry[]陣列當成圓環。從計算出來的索引位置開始,如果索引已經有資料了就判斷Key是否相同,相同就更新值。否則就直到找到一個空的位置把值放進去。取得數值的時候也類似,從計算出來的索引位置開始一個一個檢查Key是否相同,這樣hash碰撞比較多的話可能效能就不是很好。 </p><p><strong>ThreadLocal的應用</strong><code><br></code></p><p><code><span style="font-family: 微软雅黑, Microsoft YaHei;">#ThreadLocal的應用是非常廣泛的,例如Java工程師非常熟悉的Spring框架中就使用了ThreadLocal來把非線程安全的狀態性物件封裝起來,所以我們可以把絕大部分的Bean聲明為singleton作用域。我們在寫多執行緒程式碼時也可以想想是用同步的方式存取非執行緒安全的狀態性物件比較好,還是使用ThreadLocal把非執行緒安全的狀態性物件封裝起來比較好。 </span><br></code></p><p class="comments-box-content"><br></p>
以上是Java多線程之ThreadLocal的使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

熱門話題

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

Spring Boot簡化了可靠,可擴展和生產就緒的Java應用的創建,從而徹底改變了Java開發。 它的“慣例慣例”方法(春季生態系統固有的慣例),最小化手動設置
