淺談對Java中ThreadLocal類的理解,javathreadlocal

來源:互聯網
上載者:User

淺談對Java中ThreadLocal類的理解,javathreadlocal

首先要明確:ThreadLocal不是一個多線程類,或者應該叫做線程局部變數。這從ThreadLocal的JDK定義中就可以看到

public class ThreadLocal<T>extends Object


可以看出ThreadLocal只是一個普普通通的類,並沒有繼承自Thread或實現Runnable介面。

同時也可以看到ThreadLocal使用了泛型,這樣他就可以操作幾乎任何類型的資料了。下面說JDK API代碼時具體再說。

 

對此類,看看JDK API中的部分描述:

該類提供了線程局部 (thread-local) 變數。這些變數不同於它們的普通對應物,因為訪問某個變數(通過其 get 或 set 方法)的每個線程都有自己的局部變數,它獨立於變數的初始化副本。ThreadLocal 執行個體通常是類中的 private static 欄位,它們希望將狀態與某一個線程(例如,使用者識別碼 或事務 ID)相關聯。

 

1、ThreadLocal不是線程,而是縣城的一個局部變數

2、再使用時ThreadLocal通常作為類中的一個private static變數使用

3、每個線程都有一個自己的局部變數,之間互不衝突,對變數值的操作互不影響,不存在資源衝突。

總體來說,ThreadLocal作為線程的工具,為每一個線程獨立的提供了一個儲存與訪問線程變數的方式,使得不同線程間儲存的資料互不影響(因此不會出現資源競爭問題)。

 

下面就說說ThreadLocal是如何作為一個線程工具,為每個線程提供儲存與訪問線程局部變數的。

ThreadLocal提供了以下幾個API用於存取線程局部變數:

 

T get()
          返回此線程局部變數的當前線程副本中的值。
protected  T initialValue()
          返回此線程局部變數的當前線程的初始值。
void remove()
          移除此線程局部變數的值。
void set(T value)
          將此線程局部變數的當前線程副本中的值設定為指定值。

 

可以看到每個都是泛型型別的。介面的工作原理下面會具體介紹。

 

ThreadLocal類中還提供了一個靜態內部類:

 static class ThreadLocalMap

我們可以把這個ThreadLocalMap看做是一個普通的MAP類型,可以儲存一個鍵值對即可,這樣便於理解。

這個ThreadLocalMap在ThreadLocal中定義,但卻線上程類java.lang.Thread中使用。在Thread類中有以下定義:

ThreadLocal.ThreadLocalMap threadLocals = null

言外之意就是Thread類中有一個類似於Map的ThreadLocal.ThreadLocalMap 類型的變數,可以用於儲存鍵值對類型的值。

 

下面上一個簡單的例子,根據輸出結果,分析Thread與ThreadLocal工作過程。

類SerialNum

public class SerialNum {// The next serial number to be assigned    private static int nextSerialNum = 0;    public static ThreadLocal serialNum = new ThreadLocal() {        protected synchronized Object initialValue() {            return new Integer(nextSerialNum++);        }    };    public static int get() {        return ((Integer) (serialNum.get())).intValue();    }    public static void set(int val){    serialNum.set(val);    }}


 

類SerialNum中定義了一個private static ThreadLocal 類型的匿名內部類serialNum變數,其實完全可以直接new ThreadLocal,這樣寫只是為了實現重寫其initialValue方法,因為在ThreadLocal中initialValue()方法返回一個NULL值,需要說明的是,initialValue()方法是一個懶載入方法,被ThreadLocal類內部調用,並且只在第一次threadLocal類的get()被調用時調用,重寫它是為了讓局部變數有一個初始值。

SerialNum類的靜態get()和set()方法都調用ThreadLocal類的get()和set()方法。

 

類:ThreadTest

public class ThreadTest extends Thread{@Overridepublic void run() {// TODO Auto-generated method stub//先列印初始值System.out.println(Thread.currentThread().getName()+":"+SerialNum.get());//當前值加4SerialNum.set(SerialNum.get()+4);//列印加4後的值System.out.println(Thread.currentThread().getName()+":"+SerialNum.get());}public static void main(String[] args) {// TODO Auto-generated method stubThreadTest t = new ThreadTest();ThreadTest t2 = new ThreadTest();ThreadTest t3 = new ThreadTest();t.start();t2.start();t3.start();}}


 

ThreadTest中的run方法中調用了SerialNum類的靜態get()和set()方法。

輸出結果如下:

Thread-2:2
Thread-0:0
Thread-1:1
Thread-2:6
Thread-0:4
Thread-1:5

下面我們開始分析為什麼會出現這樣的結果。我們就拿線程Thread-0來說,它對應的就是t.start()啟動的線程

1、當t線程先運行時(thread-0),會調用SerialNum.get(),而其會再調用threadLocal的get方法。ThreadLocal的get()方法如下:

    public T get() {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            return (T)map.get(this);        // Maps are constructed lazily.  if the map for this thread        // doesn't exist, create it, with this ThreadLocal and its        // initial value as its only entry.        T value = initialValue();        createMap(t, value);        return value;    }

get()方法中先獲得當前線程,然後調用getMap(),API方法如下:

    ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }


 

getMap()返回當前線程中的 threadLocals變數,前面我們說過,Thread中定義了一個ThreadLocal.ThreadLocalMap類型的變數,就是這個了。

然後在get()方法中,檢查map為不為NUll,如果是NULL值,所以會調用initialValue()。而這個方法被我們重寫了,它返回了SerialNum類中定義的靜態變數nextSerialNum當前值,然後將nextSerialNum+1。因為nextSerialNum初始為0,所以返回的是0。然後調用createMap(t,value)。這個API內容如下:

void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }

可以看到 createMap中建立了一個ThreadLoadMap對象,並複製給當前線程的threadLocals變數。新建立的ThreadLocalMap類中儲存了局部變數的值value,其中鍵為this,即我們在SerialNum類中定義的那個private static ThreadLocal serialNum。

最後get()方法返回初始化好的value值0。

同時

以上就是thead-0第一次輸出列印出0的原因。

2、當線程thread-0運行到SerialNum.set(SerialNum.get()+4);這一句時,我們看發生了什麼

首先SerialNum.get()+4會取出threadLocal變數serialNum中儲存的線程局部變數值,回頭看看ThreadLocal.get()方法的介紹,因為此時當前線程中已經有了ThreadLocalMap 類型的threadLocals,所以會直接調用map.get(this);其中this依然是我們在SerialNum類中定義的那個private static ThreadLocal serialNum。

這樣就返回了map中的值。然後+4後調用ThreadLocal的set()方法。我們看看set()方法的JDK內容:

    public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }

可以看到set()方法首先也是得到當前線程,從當前線程中擷取threadLocalMap類型的map。如果map不為null,就將value儲存進去。如果為null,就在createMap中new一個ThreadLocalMap,儲存value,然後將這個 新new的threadLocalMap類型的對象賦值給當前線程的threadLocals變數(詳見createMap()方法)。
經過運行後,局部變數值就等於0+4=4了。

這也是為什麼第二次列印出的值為thread-0:4。

 

再簡單說說第二個線程thread-1。第二個線程啟動時,因為靜態變數nextSerialNum已經為1(被線程thread-0讀取後++)。所以他是從1開始列印。並且後面的+4操作與線程thread-0和thread-2並不衝突。因為:

thread-0這個線程的局部變數被儲存進threadLocalMap後,這個threadLocalMap變數被複製給這個線程(也就是thread-0自己)的threadLocals變數了。同樣的,thread-1;thread-2這兩個線程的局部變數儲存進threadLocalMap後,都分別複製給自己這個線程的threadLocals變數了。所以以後再操作都是從自己線程裡面取threadLocalMap,當然互不影響。相同的一點是這三個線程的threadLocalMap中的KEY都為同一個ThreadLocal對象。

通過這個例子還能看出一點,局部變數初始化後,就再和開始那個nextSerialNum沒關係了,這應該就是JDK API中描述的:訪問一個變數(通過其get 或 set 方法)的每個線程都有自己的局部變數,它獨立於變數的初始化副本

以上就是自己對ThreadLocal使用的淺談。開始怕說不清楚,就想寫的直白點。後來自己都感覺囉嗦了。

 


對於java的ThreadLocal的問題

ThreadLocal把資源作成當前線程內共用 不管是跨包跨類跨方法 這個資源都可以被訪問
線程類的private屬性 只能在本類使用 其它類無法使用
 
java裡面的ThreadLocal具體做神馬用的?

個人理解,我們現在軟體經常會分層,比如MVC ,類似這種橫向分層,而ThreadLocal會提供一種方式,方便的在同一個線程範圍內,提供一個儲存空間,供我們使用,實現縱向的儲存結構,便於我們在同一個線程範圍內,隨時取得我們在另外一個層面存放的資料。
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.