這篇文章帶給大家的內容是關於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中文網其他相關文章!