ThreadLocal的使用以及實現原理解析

2021-09-06 20:11:50 字數 3013 閱讀 6473

前面的文章裡,我們學習了有關鎖的使用,鎖的機制是保證同一時刻只能有乙個執行緒訪問臨界區的資源,也就是通過控制資源的手段來保證執行緒安全,這固然是一種有效的手段,但程式的執行效率也因此大大降低。那麼,有沒有更好的方式呢?答案是有的,既然鎖是嚴格控制資源的方式來保證執行緒安全,那我們可以反其道而行之,增加更多資源,保證每個執行緒都能得到所需物件,各自為營,互不影響,從而達到執行緒安全的目的,而threadlocal便是採用這樣的思路。

threadlocal翻譯成中文的話大概可以說是:執行緒區域性變數,也就是只有當前執行緒能夠訪問。它的設計作用是為每乙個使用該變數的執行緒都提供乙個變數值的副本,每個執行緒都是改變自己的副本並且不會和其他執行緒的副本衝突,這樣一來,從執行緒的角度來看,就好像每個執行緒都擁有了該變數。

下面是乙個簡單的例項:

public class threadlocaldemo 

};public static class myrunnable implements runnable catch (interruptedexception e)

int value = local.get();

system.out.println(thread.currentthread().getname() + ":" + value);

local.set(value + 1);}}

}public static void main(string args)

}

上面的**不難理解,首先是定義了乙個名為local的threadlocal變數,並初識變數的值為0,然後是定義了乙個實現runnable介面的內部類,在其run方法中對local的值做讀取和加1的操作,最後是main方法中開啟兩個執行緒來執行內部類例項。

以上就是**的大概邏輯,執行main函式後,程式的輸出結果如下:

thread-0:0

thread-1:0

thread-1:1

thread-0:1

thread-1:2

thread-0:2

從結果可以看出,雖然兩個執行緒都共用乙個runnable例項,但兩個執行緒中所展示的threadlocal的資料值並不會相互影響,也就是說這種情況下的local變數儲存的資料相當於是執行緒安全的,只能被當前執行緒訪問。

那麼threadlocal內部是怎麼保證物件是執行緒私有的呢?毫無疑問,答案需要從原始碼中查詢。回顧前面的**,可以發現其中呼叫了threadlocal的兩個方法setget,我們就從這兩個方法入手。

先看set()的原始碼:

public void set(t value) 

threadlocalmap getmap(thread t)

void createmap(thread t, t firstvalue)

set的**邏輯比較簡單,主要是把值設定到當前執行緒的乙個threadlocalmap物件中,而threadlocalmap可以理解成乙個map,它是定義在thread類中內部的成員,初始化是為null,

threadlocal.threadlocalmap threadlocals = null;
不過,與常見的map實現類,如hashmap之類的不同的是,threadlocalmap中的entry是繼承於weakreference類的,保持了對 「鍵」 的弱引用和對 「值」 的強引用,這是類的原始碼:

static class threadlocalmap 

}//省略剩下的原始碼

....................

}

從原始碼中中可以看出,entry建構函式中的引數k就是threadlocal例項,呼叫super(k) 表明對k是弱引用,使用弱引用的原因在於,當沒有強引用指向 threadlocal 例項時,它可被**,從而避免記憶體洩露,那麼為何需要防止記憶體洩露呢?原因下面會說到。

接著說set方法的邏輯,當呼叫set方法時,其實是將資料寫入threadlocals這個map物件中,這個map的key為threadlocal當前物件,value就是我們存入的值。而threadlocals本身能儲存多個threadlocal物件,相當於乙個threadlocal集合。

接著看get()的原始碼:

public t get() 

}//設定初識值到threadlocal中並返回

return setinitialvalue();

}private t setinitialvalue()

get方法的邏輯也是比較簡單的,就是直接獲取當前執行緒的threadlocalmap物件,如果該物件不為空就返回它的value值,否則就把初始值設定到threadlocal中並返回。

然而,該方案雖然能保證執行緒私有,但卻會占用大量的記憶體,因為每個執行緒都維護著乙個map,當訪問某個threadlocal變數後,執行緒會在自己的map內維護該threadlocal變數與具體實現的對映,如果這些對映一直存在,就表明threadlocal 存在引用的情況,那麼系統gc就無法**這些變數,可能會造成記憶體洩露。

針對這種情況,上面所說的threadlocalmap中entry的弱引用就起作用了。

最後,總結一下threadlocal和同步機制之間的區別吧。

實現機制:

同步機制採用了「以時間換空間」的方式,控制資源保證同一時刻只能有乙個執行緒訪問。

threadlocal採用了「以空間換時間」的方式,為每乙個執行緒都提供乙份變數的副本,從而實現同時訪問而互不影響,但因為每個執行緒都維護著乙份副本,對記憶體空間的占用會增加。

資料共享:

同步機制是對公共資源做控制訪問的方式來保證執行緒安全,但資源仍是共享狀態,可用於執行緒間的通訊;

threadlocal是每個執行緒都有自己的資源(變數)副本,互相之間不影響,也就不存在共享的說法了。

ThreadLocal使用介紹

首先有幾點需要大家清楚的 1 threadlocal 只是對需要儲存的物件的管理,而儲存實際是由當前thread 負責。個人理解為threadlocal 是乙個操作thread.threadlocals 的工具。2 使用threadlocal 可以使物件達到執行緒隔離的目的。同乙個threadloc...

ThreadLocal使用案例

本文藉由併發環境下使用執行緒不安全的 dateformat優化案例,幫助大家理解threadlocal.public class dateutil catch parseexception e 首先分析下 該處的函式parseymdhms 使用了synchronized修飾,意味著該操作是執行緒不安...

ThreadLocal的作用與使用

在我們程式設計時,如果遇到多個執行緒訪問同乙個變數應該怎樣實現?有人說使用同步。是的同步可以解決這種問題,但它是有弊端的,涉及到何時加鎖與釋放鎖等並且執行緒訪問鎖時需要等待,這樣很浪費時間。有乙個更好的方案就是使用threadlocal工具類,之前參加了乙個專案,本專案涉及到分庫,在業務進行中需要根...