到底什麼是線程的不安全?爲何會存在線程的不安全?線程的不安全其實就是多個線程併發的去操做同一共享變量沒用作同步所產生意料以外的結果。那是如何體現出來的呢?咱們看下面的一個很是經典的例子:兩個操做員同時操做同一個銀行帳戶,A操做員存錢,100B操做員取錢50。咱們看一下流程。編程
兩個操做員同時處理,沒用作同步這個時候咱們發現銀行帳戶最終餘額剩餘950元,在咱們想的最終結果銀行帳戶應該剩餘1000+100-50=1050元,在執行過程當中咱們沒有加鎖,最終致使了運行結果偏離預期。那麼如何解決的?通常的解決措施就是加鎖,加同步鎖因此這就須要使用者必定要知道鎖是什麼。咱們來看一下加鎖以後的效果是否是咱們所預期的。安全
在添加同步鎖後咱們能夠看到,A操做員和B操做員同時去操做帳戶,可是A先搶佔到資源,因此B就只能等待A操做員釋放鎖才能去操做銀行帳戶,那麼最終結果是咱們所預期的嗎?答案是的。多線程
同步的話通常都是加鎖,若是如今我想建立多個線程每一個線程都是訪問的本身的變量呢?各個線程之間毫無關聯?併發
答案是有的。工具
ThreadLocal
是JDK提供的,它提供了線程本地變量。什麼是線程本地變量呢?其實就是你建立了一個Threadlocal
變量,每一個訪問Threadlocal
變量的線程都有一個本地副本。咱們看下面的圖:this
從上面看出你建立一個
ThreadLocal
變量,每一個訪問該的線程都會複製到本身的本地,因此線程操做的都是本地的副本,這也就是說每一個線程都是操做的本身本地的變量,那就完美的避免了線程安全的問題。線程
在這裏還有一個問題。我在寫這篇文章的時候看過不少文章,總的來講就是ThreadLocal
就是爲了解決多線程併發問題而提供的一種方法,還有一種解釋就是ThreadLocal
的最終目的就是爲了解決多線程訪問共享資源所產生的。真的對嗎?ThreadLocal
並無共享那麼從何而來的同步呢?code
在看了Java併發編程之美
後我所理解的Threadlocal
提供了線程本地變量的副本,每一個線程實際操做的時本身本地的變量副本,也就是說該變量副本只能當前線程訪問,就不存在多個線程共享的問題,從Threadlocal
名字咱們也能看出本地線程
。那那那它也就不存在去解決併發問題了。cdn
咱們來看下面的例子。blog
輸出結果:
Thread[Thread-1,5,main]====57
Thread[Thread-0,5,main]====75
建立了兩個線程,它們都在threadlocal
上面都set了一個隨機數,咱們看最後得輸出結果每一個都是不一樣得值,那麼咱們若是把threadlocal
替換成一個集合會發生什麼,因爲兩個線程時上個線程生成的隨機數57會被第二個線程覆蓋掉,而在Threadlocal
中兩個線程都是操做的本身的本地副本,那麼兩個線程互不影響都沒法操控到對方的數據,所以它們存取的都是不一樣的值。
那麼Threadlocal
是如何實現的呢?在研究Threadlocal
的實現原理咱們先看一下Thread
的內部屬性。
Threadlocal
的值 inheritableThreadLocals等到後面再說。
在Thread
的內部屬性中咱們看到了這兩個默認爲null的屬性,threadLocals用來保存Threadlocal
的本地副本,默認是爲null只有調用Threadlocal
的set時纔會建立。也就是說Threadlocal
就相似一個工具,它的做用就是把value的值經過set存在線程每一個線程的threadLocals 中,只要線程一直存在threadLocals 也就一直存在。因此當不須要使用本地變量的時候能夠調用Threadlocal
的remove來清空本地變量。而threadLocals 爲何繼承魚ThreadLocalMap呢?ThreadLocalMap是一個定製的HashMap,而使用Map的緣由就是能夠每一個線程關聯多個Threadlocal
變量。
咱們來看一下set方法是如何實現的。
能夠看出流程很是簡單,首先獲取當前線程而後在進行下一步操做,咱們在看一下getMap作了什麼
getMap主要就是返回了當前threadLocals的屬性。那若是map爲空呢?
若是map爲空的話就直接建立一個新的ThreadLocalMap。
咱們來看一下流程圖。
看一下Get方法
首先根據當前線程獲取實例若是存在就返回,若是不存在就先初始化一個空值,而後判斷若是當前threadLoacals不爲空就直接set一個空,不然就建立一個變量。
remove方法相對來講比較簡單。
Threadlocal
的實現原理其實就是經過set把value set到線程的threadlocals屬性中,threadlocals
類型是Map其中的Key就是Threadlocal
的this引用,value就是咱們所set的值,若是當前線程不銷燬的話threadlocals
會一直存在。一直存在的話可能會形成內存溢出,因此使用完以後儘可能remove一下。不過在這裏又有一個問題那就是若是個人線程想要讀取主線程的變量要怎麼作?咱們上面的例子都是設置的新建立的線程,那麼如今我在主線程中set一個值,這個時候我在新建立的線程中能夠讀取到嗎?答案是不能夠,由於Threadlocal
不支持繼承性。
咱們看下面的例子:
輸出結果:
Thread[Thread-0,5,main]====null
也就是說Threadlocal
不支持繼承性,主線程設置了值,在子線程中是獲取不到的。那我如今想要獲取主線程裏面的值要怎麼作?
Threadlocal
是實現不了的,不過Threadlocal
有一個子類能夠實現。InheritableThreadLocal
,InheritableThreadLocal
是Threadlocal
的實現,咱們來看一個簡單的例子。
輸出結果:
Thread[Thread-0,5,main]====1000
運行結果發現子線程是能夠獲取到主線程設置的值的,那它是如何實現的?
咱們看一下代碼實現:
InheritableThreadLocal
是繼承Threadlocal
的,而且把threadlocals
給替換成inheritableThreadLocals
了因此上面的inheritableThreadLocals
我要留在最後說,那麼替換成inheritableThreadLocals
後子線程就能夠獲取到主線程設置的屬性了嗎?咱們在看一下Thread的實現。
看Thread
的初始化方法能夠看出,先獲取了當前線程(主線程)判斷主線程的inheritableThreadLocals
不爲空的話就調用createInheritedMap方法賦值給子線程中的inheritableThreadLocals
。具體這裏解釋太多。有機會在寫一篇文章來解釋。