歡迎光臨
每天分享高質量文章

一個故事講明白執行緒的私家領地:ThreadLocal

來自:碼農翻身(微訊號:coderising)

張大胖上午遇到了一個棘手的問題,他在一個AccountService中寫了一段類似這樣的程式碼:

Context ctx = new Context();
ctx.setTrackerID(.....)

然後這個AccountService 呼叫了其他Java類,不知道經過了多少層呼叫以後,最終來到了一個叫做AccountUtil的地方,在這個類中需要使用Context中的trackerID來做點兒事情:

很明顯,這個AccountUtil沒有辦法拿到Context物件, 怎麼辦?

張大胖想到,要不把Context物件一層層地傳遞下去,這樣AccountUtil不就可以得到了嗎?

可是這麼做改動量太大!涉及到的每一層函式呼叫都得改動,有很多類都不屬於自己的小組管理,還得和別人協調。 

更要命的是有些類根本就沒有原始碼,想改都改不了。

這也難不住我,張大胖想:可以把那個set/get TrackerID的方法改成靜態(static)的,這樣不管跨多少層呼叫都沒有問題!

public class Context{
   public static String getTrackerID(){
       ......
   }
   public static void setTrackerID(String id){
       ......
   }
}

這樣就不用一層層地傳遞了,Perfect!

張大胖得意洋洋地把程式碼提交給Bill做Review。 

Bill看了一眼就指出了致命的問題: 多執行緒併發的時候出錯!

張大胖恨不得找個地縫鑽進去:又栽在多執行緒上面了,這次犯的還是低階錯誤!

執行緒1呼叫了Context.setTrackerID(), 執行緒2 也呼叫了Context.setTrackerID(),資料互相改寫,不出亂子才怪。

張大胖感慨地說:“像我這樣中情況,需要在某處設定一個值,然後經過重重方法呼叫,到了另外一處把這個值取出來,又要執行緒安全,實在是不好辦啊, 對了,我能不能把這個值就放到執行緒中? 讓執行緒攜帶著這個值到處跑,這樣我無論在任何地方都可以輕鬆獲得了!”

Bill說:“有啊,每個執行緒都有一個私家領地! 在Thread這個類中有個專門的資料結構,你可以放入你的TrackerID,然後到任何地方都可以把這個TrackerID給取出來。”

“這麼好? ” 

張大胖開啟JDK中的Thread類,仔細檢視,果然在其中有個叫做threadLocals的變數,還是個Map型別 , 但是在Thread類中卻沒有對這個變數操作的方法。 

看到張大胖的疑惑,Bill說:“也許你註意到了,這個變數不是透過Thread的訪問的,對他的訪問委託給了ThreadLocal這個類。

“那我怎麼使用它?”

“非常簡單, 你可以輕鬆建立一個ThreadLocal類的實體:

ThreadLocal threadLocalA= new ThreadLocal();

執行緒1: threadLocalA.set("1234");
執行緒2: threadLocalA.set("5678");

像‘1234’, ‘5678’這些值都會放到自己所屬的執行緒物件中。”

“等你使用的時候,可以這麼辦:”

執行緒1: threadLocalA.get()  --> "1234"
執行緒2: threadLocalA.get() --> "5678"

“明白了,相當於把各自的資料放入到了各自Thread這個物件中去了,每個執行緒的值自然就區分開了。 可是我不明白的是為什麼那個資料結構是個map 呢?”

“你想想,假設你建立了另外一個threadLocalB:”

ThreadLocal threadLocalB = new ThreadLocal();

執行緒1: threadLocalB.set(30);
執行緒2: threadLocalB.set(40);

那執行緒物件的Map就起到作用了:

“明白了,這個私家領地還真是好用,我現在就把我那個Context給改了,讓它使用ThreadLocal:”

public class Context {
   private static final ThreadLocal mThreadLocal
       = new ThreadLocal();

   public static void setTrackerID(String id) {
       mThreadLocal.set(id);
   }  
   public static String getTrackerID() {
       return mThreadLocal.get();
   }  

}

小結:

ThreadLocal這個名字起得有點讓人誤解, 很容易讓人認為是“本地執行緒”, 其實是用來維護本執行緒的變數。 對照著上面的原理講解,我想大家可以自行去看ThreadLocal的原始碼,輕鬆理解。

ThreadLocal 並不僅僅是Java中的概念,其他語言例如Python,C#中也有,作用類似。

ThreadLocal在日常工作中用得不多,但是在框架(如Spring)中是個基礎性的技術,在事務管理,AOP等領域都能找到。

(完)


●本文編號610,以後想閱讀這篇文章直接輸入610即可

●輸入m獲取文章目錄

推薦↓↓↓

 

演演算法與資料結構

更多推薦18個技術類微信公眾號

涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

贊(0)

分享創造快樂