ThreadLocal中怎么實現(xiàn)線程專屬的變量

ThreadLocal中怎么實現(xiàn)線程專屬的變量,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、重慶小程序開發(fā)、集團企業(yè)網(wǎng)站建設(shè)等服務(wù)項目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了益陽免費建站歡迎大家使用!

一、ThreadLocal 簡介

ThreadLocal是 java  提供的一個方便對象在本線程內(nèi)不同方法中傳遞和獲取的類。用它定義的變量,僅在本線程中可見和維護,不受其他線程的影響,與其他線程相互隔離。

那 ThreadLocal 到底解決了什么問題,又適用于什么樣的場景?

  • This class provides thread-local variables. These variables differ from their  normal counterparts in that each thread that accesses one (via its get or set  method) has its own, independently initialized copy of the variable. ThreadLocal  instances are typically private static fields in classes that wish to associate  state with a thread (e.g., a user ID or Transaction ID). Each thread holds an  implicit reference to its copy of a thread-local variable as long as the thread  is alive and the ThreadLocal instance is accessible; after a thread goes away,  all of its copies of thread-local instances are subject to garbage collection  (unless other references to these copies exist).

核心意思是

ThreadLocal 提供了線程本地的實例。它與普通變量的區(qū)別在于,每個使用該變量的線程都會初始化一個完全獨立的實例副本。ThreadLocal  變量通常被private static修飾。當(dāng)一個線程結(jié)束時,它所使用的所有 ThreadLocal 相對的實例副本都可被回收。

總的來說,ThreadLocal  適用于每個線程需要自己獨立的實例且該實例需要在多個方法中被使用,也即變量在線程間隔離而在方法或類間共享的場景。后文會通過實例詳細(xì)闡述該觀點。另外,該場景下,并非必須使用  ThreadLocal ,其它方式完全可以實現(xiàn)同樣的效果,只是 ThreadLocal 使得實現(xiàn)更簡潔。

二、ThreadLocal 使用

ThreadLocal 通過 set 方法可以給變量賦值,通過 get 方法獲取變量的值。當(dāng)然,也可以在定義變量時通過  ThreadLocal.withInitial 方法給變量賦初始值,或者定義一個繼承 ThreadLocal 的類,然后重寫 initialValue  方法。

下面通過如下代碼說明 ThreadLocal 的使用方式:

public class TestThreadLocal {     private static ThreadLocal<StringBuilder> builder = ThreadLocal.withInitial(StringBuilder::new);      public static void main(String[] args)     {         for (int i = 0; i < 5; i++)         {             new Thread(() -> {                 String threadName = Thread.currentThread().getName();                 for (int j = 0; j < 3; j++)                 {                     append(j);                     System.out.printf("%s append %d, now builder value is %s, ThreadLocal instance hashcode is %d, ThreadLocal instance mapping value hashcode is %d\n", threadName, j, builder.get().toString(), builder.hashCode(), builder.get().hashCode());                 }                  change();                 System.out.printf("%s set new stringbuilder, now builder value is %s, ThreadLocal instance hashcode is %d, ThreadLocal instance mapping value hashcode is %d\n", threadName, builder.get().toString(), builder.hashCode(), builder.get().hashCode());             }, "thread-" + i).start();         }     }      private static void append(int num) {         builder.get().append(num);     }      private static void change() {         StringBuilder newStringBuilder = new StringBuilder("HelloWorld");         builder.set(newStringBuilder);     } }

在例子中,定義了一個 builder 的 ThreadLocal 對象,然后啟動 5 個線程,分別對 builder  對象進(jìn)行訪問和修改操作,這兩個操作放在兩個不同的函數(shù) append、change 中進(jìn)行,兩個函數(shù)訪問 builder  對象也是直接獲取,而不是放入函數(shù)的入?yún)⒅袀鬟f進(jìn)來。

代碼輸出如下:

thread-0 append 0, now builder value is 0, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 566157654 thread-0 append 1, now builder value is 01, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 566157654 thread-4 append 0, now builder value is 0, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 654647086 thread-3 append 0, now builder value is 0, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 1803363945 thread-2 append 0, now builder value is 0, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 1535812498 thread-1 append 0, now builder value is 0, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 2075237830 thread-2 append 1, now builder value is 01, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 1535812498 thread-3 append 1, now builder value is 01, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 1803363945 thread-4 append 1, now builder value is 01, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 654647086 thread-0 append 2, now builder value is 012, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 566157654 thread-0 set new stringbuilder, now builder value is HelloWorld, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 1773033190 thread-4 append 2, now builder value is 012, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 654647086 thread-4 set new stringbuilder, now builder value is HelloWorld, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 700642750 thread-3 append 2, now builder value is 012, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 1803363945 thread-3 set new stringbuilder, now builder value is HelloWorld, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 1706743158 thread-2 append 2, now builder value is 012, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 1535812498 thread-2 set new stringbuilder, now builder value is HelloWorld, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 1431127699 thread-1 append 1, now builder value is 01, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 2075237830 thread-1 append 2, now builder value is 012, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 2075237830 thread-1 set new stringbuilder, now builder value is HelloWorld, ThreadLocal instance hashcode is 796465865, ThreadLocal instance mapping value hashcode is 1970695360
  • 從輸出中 1~6 行可以看出,不同線程訪問的是同一個 builder 對象(不同線程輸出的 ThreadLocal instance hashcode  值相同),但是每個線程獲得的 builder 對象存儲的實例 StringBuilder 不同(不同線程輸出的 ThreadLocal instance  mapping value hashcode值不相同)。

  • 從輸出中1~2、9~10 行可以看出,同一個線程中修改 builder 對象存儲的實例的值時,并不會影響到其他線程的 builder  對象存儲的實例(thread-4 線程改變存儲的 StringBuilder 的值并不會引起 thread-0 線程的 ThreadLocal instance  mapping value hashcode 值發(fā)生改變)

  • 從輸出中 9~13 行可以看出,一個線程對 ThreadLocal 對象存儲的值發(fā)生改變時,并不會影響其他的線程(thread-0 線程調(diào)用 set  方法改變本線程 ThreadLocal 存儲的對象值,本線程的 ThreadLocal instance mapping value hashcode  發(fā)生改變,但是 thread-4 的 ThreadLocal instance mapping value hashcode 并沒有因此改變)。

三、ThreadLocal 原理

ThreadLocal 能在每個線程間進(jìn)行隔離,其主要是靠在每個 Thread 對象中維護一個 ThreadLocalMap  來實現(xiàn)的。因為是線程中的對象,所以對其他線程不可見,從而達(dá)到隔離的目的。那為什么是一個 Map 結(jié)構(gòu)呢。主要是因為一個線程中可能有多個 ThreadLocal  對象,這就需要一個集合來進(jìn)行存儲區(qū)分,而用 Map 可以更快地查找到相關(guān)的對象。ThreadLocalMap 是 ThreadLocal  對象的一個靜態(tài)內(nèi)部類,內(nèi)部維護一個 Entry 數(shù)組,實現(xiàn)類似 Map 的 get 和 put 等操作,為簡單起見,可以將其看做是一個 Map,其中 key 是  ThreadLocal 實例,value 是 ThreadLocal 實例對象存儲的值。

ThreadLocal中怎么實現(xiàn)線程專屬的變量

ThreadLocalMap

四、ThreadLocal 適用場景

如上文所述,ThreadLocal 適用于如下場景:

每個線程需要有自己單獨的實例,如實現(xiàn)每個線程單例類或每個線程上下文信息(例如事務(wù)ID)。

ThreadLocal 適用于變量在線程間隔離且在方法間共享的場景,提供了另一種擴展 Thread  的方法。如果要保留信息或?qū)⑿畔囊粋€方法調(diào)用傳遞到另一個方法,則可以使用 ThreadLocal 進(jìn)行傳遞。

由于不需要修改任何方法,因此可以提供極大的靈活性。

1、案例一

這里一個處理 flag 的類,通過 ThreadLocal 使用,可以保證每個請求都擁有唯一的一個追蹤標(biāo)記。

public class TestFlagHolder {    private final static ThreadLocal<String> TEST_FLAG = new ThreadLocal<>();    public static void set(String value) {     TEST_FLAG.set(value);   }    public static String get() {     return TEST_FLAG.get();   }    public static String get4log() {     if (TEST_FLAG.get() == null) {       return "-";     }     return TEST_FLAG.get();   }    public static void remove() {     TEST_FLAG.remove();   }  }

2、案例二

在同一線程中 trace 信息的傳遞:

ThreadLocal<String> traceContext = new ThreadLocal<>();  String traceId = Tracer.startServer(); traceContext.set(traceId) //生成trace信息 傳入threadlocal ... Tracer.startClient(traceContext.get()); //從threadlocal獲取trace信息 Tracer.endClient(); ... Tracer.endServer();

3、案例三

給同一個請求的每一行日志增加一個相同的標(biāo)記。這樣,只要拿到這個標(biāo)記就可以查詢到這個請求鏈路上所有步驟的耗時了,我們把這個標(biāo)記叫做  requestId,我們可以在程序的入口處生成一個 requestId,然后把它放在線程的上下文中,這樣就可以在需要時隨時從線程上下文中獲取到  requestId 了。

簡單的代碼實現(xiàn)就像下面這樣:

String requestId = UUID.randomUUID().toString(); ThreadLocal<String> tl = new ThreadLocal<String>(){     @Override     protected String initialValue() {         return requestId;     } }; //requestId存儲在線程上下文中 long start = System.currentTimeMillis(); processA(); Logs.info("rid : " + tl.get() + ", process A cost " + (System.currentTimeMillis() - start)); // 日志中增加requestId start = System.currentTimeMillis(); processB(); Logs.info("rid : " + tl.get() + ", process B cost " + (System.currentTimeMillis() - start)); start = System.currentTimeMillis(); processC(); Logs.info("rid : " + tl.get() + ", process C cost " + (System.currentTimeMillis() - start));

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。

標(biāo)題名稱:ThreadLocal中怎么實現(xiàn)線程專屬的變量
本文URL:http://muchs.cn/article40/gheheo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動網(wǎng)站建設(shè)、企業(yè)建站、網(wǎng)站維護域名注冊、響應(yīng)式網(wǎng)站軟件開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

營銷型網(wǎng)站建設(shè)