使用ThreadLocal變數的時機和方法
併發程式設計中,一個重要的內容是資料共享。當你建立了實現Runnable介面的執行緒,然後開啟使用相同Runnable例項的各種Thread物件,所有 的執行緒便共享定義在Runnable物件中的屬性。也就是說,當你在一個執行緒中改變任意屬性時,所有的執行緒都會因此受到影響,同時會看到第一個執行緒修改後的值。有時我們希望如此,比如:多個執行緒增大或減小同一個計數器變數;但是,有時我們希望確保每個執行緒,只能工作在它自己的執行緒例項的拷貝上,同時不會影 響其他執行緒的資料。
使用ThreadLocal的時機
舉個例子,想象你在開發一個電子商務應用,你需要為每一個控制器處理的顧客請求,生成一個唯一的事務ID,同時將其傳到管理器或DAO的業務方法中,以便記錄日誌。一種方案是將事務ID作為一個引數,傳到所有的業務方法中。但這並不是一個好的方案,它會使程式碼變得冗餘。
你可以使用ThreadLocal型別的變數解決這個問題。首先在控制器或者任意一個前處理器攔截器中生成一個事務ID,然後在ThreadLocal中 設定事務ID,最後,不論這個控制器呼叫什麼方法,都能從threadlocal中獲取事務ID。而且這個應用的控制器可以同時處理多個請求,同時在框架 層面,因為每一個請求都是在一個單獨的執行緒中處理的,所以事務ID對於每一個執行緒都是唯一的,而且可以從所有執行緒的執行路徑獲取。
擴充套件閱讀:與JAX-RS ResteasyProviderFactory共享上下文資料(ThreadLocalStack例項)
ThreadLocal類
Java併發API為使用ThreadLocal類的區域性執行緒變數提供了一個簡潔高效的機制,
public class ThreadLocal<T> extends Object {...}
這個類提供了一個區域性執行緒變數。這些變數不同於其所對應的常規變數,對於常規變數,每個執行緒只能訪問(通過get或set方法)其自身所擁有的,獨立初始化變數拷貝。在一個類中,ThreadLocal型別的例項是典型的私有、靜態(private static)欄位,因為我們可以將其作為執行緒的關聯狀態(比如:使用者ID或者事務ID)
這個類有以下方法:
- get():返回當前執行緒拷貝的區域性執行緒變數的值。
- initialValue():返回當前執行緒賦予區域性執行緒變數的初始值。
- remove():移除當前執行緒賦予區域性執行緒變數的值。
- set(T value):為當前執行緒拷貝的區域性執行緒變數設定一個特定的值。
怎樣使用ThreadLocal?
下面的例子使用兩個區域性執行緒變數,即threadId和startDate。它們都遵循推薦的定義方法,即“private static”型別的欄位。threadId用來區分當前正在執行的執行緒,startDate用來獲取執行緒開啟的時間。上面的資訊將列印到控制檯,以此驗 證每一個執行緒管理他自己的變數拷貝。
class DemoTask implements Runnable { // Atomic integer containing the next thread ID to be assigned private static final AtomicInteger nextId = new AtomicInteger(0); // Thread local variable containing each thread's ID private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return nextId.getAndIncrement(); } }; // Returns the current thread's unique ID, assigning it if necessary public int getThreadId() { return threadId.get(); } // Returns the current thread's starting timestamp private static final ThreadLocal<Date> startDate = new ThreadLocal<Date>() { protected Date initialValue() { return new Date(); } }; @Override public void run() { System.out.printf("Starting Thread: %s : %sn", getThreadId(), startDate.get()); try { TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf("Thread Finished: %s : %sn", getThreadId(), startDate.get()); } }
現在要驗證變數本質上能夠維持其自身狀態,而與多執行緒的多次初始化無關。我們首先需要建立執行這個任務的三個執行緒,然後開啟執行緒,接著驗證它們列印到控制檯中的資訊。
Starting Thread: 0 : Wed Dec 24 15:04:40 IST 2014 Thread Finished: 0 : Wed Dec 24 15:04:40 IST 2014 Starting Thread: 1 : Wed Dec 24 15:04:42 IST 2014 Thread Finished: 1 : Wed Dec 24 15:04:42 IST 2014 Starting Thread: 2 : Wed Dec 24 15:04:44 IST 2014 Thread Finished: 2 : Wed Dec 24 15:04:44 IST 2014
在上面的輸出中,列印出的宣告序列每次都在變化。我已經把它們放到了序列中,這樣對於每一個執行緒例項,我們都可以清楚地辨別出,區域性執行緒變數保持著安全狀態,而絕不會混淆。自己嘗試下!
區域性執行緒通常使用在這樣的情況下,當你有一些物件並不滿足執行緒安全,但是你想避免在使用synchronized關鍵字、塊時產生的同步訪問,那麼,讓每個執行緒擁有它自己的物件例項。
注意:區域性變數是同步或區域性執行緒的一個好的替代,它總是能夠保證執行緒安全。唯一可能限制你這樣做的是你的應用設計約束。
警告:在webapp伺服器上,可能會保持一個執行緒池,那麼ThreadLocal變數會在響應客戶端之前被移除,因為當前執行緒可能被下一個請求重複使用。而 且,如果在使用完畢後不進行清理,它所保持的任何一個對類的引用—這個類會作為部署應用的一部分載入進來—將保留在永久堆疊中,永遠不會被垃圾回收機制回收。
相關文章
- Python技術分享:深入理解ThreadLocal變數的功能和使用Pythonthread變數
- 關於ThreadLocal變數的一個坑thread變數
- 不使用臨時變數交換兩個變數的值變數
- 變數的分類(臨時(本地)變數、環境變數、全域性變數和系統變數)變數
- java 併發,為執行緒建立本地變數 ThreadLocal的使用Java執行緒變數thread
- 變數的定義和使用變數
- @requestmapping--springmvc註解的使用變數和全域性部*替代變數方法APPSpringMVC變數
- 類變數的初始化時機總是處於例項變數的初始化時機之前!變數
- MySQL中變數的定義和變數的賦值使用MySql變數賦值
- TensorFlow——共享變數的使用方法變數
- 如何不使用臨時變數,交換兩個變數的值。變數
- JavaScript兩個變數交換值(不使用臨時變數)JavaScript變數
- 在php中使用繫結變數的方法(Oracle SQL共享的機制)(轉)PHP變數OracleSQL
- ThreadLocal的使用thread
- Java - 24 類變數和類方法Java變數
- QT 全域性變數使用方法QT變數
- Python的區域性變數和全域性變數使用解惑Python變數
- 表變數和臨時表的差別 (以前把表變數叫成變數表了,哎。。。)變數
- SQL Server中的臨時表和表變數SQLServer變數
- 面試中的 ThreadLocal 原理和使用場景面試thread
- 函式中的私有變數和特權方法函式變數
- 15 個變數和方法命名的最佳實踐變數
- 再議臨時表和表變數變數
- c語言 - 交換兩個變數(不建立臨時變數)兩種方法C語言變數
- ThreadLocal 原理和使用場景分析thread
- 使用繫結變數窺探後的cardinality和selectivity的計算方法變數
- 使用var和不使用var宣告變數的區別變數
- 修改全域性變數時,可變型別和不可變型別的區別變數型別
- Pythonrandom模組(獲取隨機數)常用方法和使用例子Pythonrandom隨機
- 課時3:小插曲之變數和字串變數字串
- go 的變數使用Go變數
- MySQL變數的使用MySql變數
- 變數的基本使用變數
- +load和+initialize方法呼叫時機
- 方法的過載、可變形參的方法、方法的引數值傳遞機制、遞迴方法遞迴
- Python中私有變數和私有方法Python變數
- 生成總和固定 可變範圍的隨機數隨機
- 深入瞭解 Java 方法和引數的使用方法Java