Java程式碼質量改進之:使用ThreadLocal維護執行緒內部變數

陸敏技發表於2018-08-04

       在上文中,《Java程式碼質量改進之:同步物件的選擇》,我們提出了一個場景:火車站有3個售票視窗,同時在售一趟列車的100個座位。我們通過鎖定一個靠譜的同步物件,完成了上面的功能。

       現在,讓我們反過來,每個視窗負責一趟車。比如一號視窗就賣1號列車的票,二號視窗就賣2號列車的票。不過它們需要同時開始賣票。

 

一:ThreadLocal的最簡應用

       首先,既然是各賣各的火車了,那麼,就不需要同步了。於是程式碼又迴歸到:

 

       但是當前的程式碼肯定是不對的,每個執行緒訪問的都是同一個火車的ticket,並且還會出現超售現象。要保證每new一個視窗出來,就有一趟自己的列車,我們就可以用到ThreadLocal物件了。

 

       讓我們首先替換掉ticket變數,改為:

 

       然後,售票的程式碼改為:

 

      雖然ticket依然是一個static變數,但是,執行程式你會發現,新起一個執行緒,不同的執行緒還是會擁有自己的ticket,不會互相干擾。也就是實現了每個視窗賣自己那趟車的目標。

 

二:ThreadLocal VS 例項變數

      每一個程式設計師都應該是槓精。為什麼,因為回過神來的我們發現,只要回到第一段程式碼中,把ticket中的static去掉,就能達到同樣的目的:

 

       試下上面的程式碼,是不是也能達到各賣各的目的?

       我們是腦袋被門板擠了,才想出來一個TheadLocal這樣的複雜方案嗎?

       如果單純說上面的這段程式碼,是的。但是,還有很多的場合,是ThreadLocal的用武之處。比如,我們並不永遠使用extends Thead的方式來寫多執行緒,我們還可能用implements Runnable的方式來寫多執行緒(ps:還有更多的寫法哦),如下:

 

       而在這種情況下,我們就不得不使用TheadLocal了,這裡就不放出程式碼了,大家可以試一下。

       甚至,更進一步的,我們是不是能夠把ticket這個變數放進方法內部呢,如果放入方法

內部的話,我們同樣也是必須要使用ThreadLocal才能達到實現目的,如下:

 

      總之,簡單來說:當要執行的程式碼本身不是很方便訪問當前的執行緒例項的時候,就是ThreadLocal的用武之地。

 

三:ThreadLocal的應用場景

      ThreadLocal有這樣一些應用場景,比如連線池管理、會話管理等等。

       在連線池的管理中,當我們需要獲取一個連線,就應該為每一次獲取給出不同的連線。在web應用中,請求是被執行緒池管理的,也就是說獲取連線這個行為不是單執行緒行為,所以我們最好就要設計成不同的執行緒不能獲取同一個連線,要保證能做到這樣,就應該使用ThreadLocal了。

      可能有人會表示,那不能設計成例項變數嗎?答案是:不能。因為,在web應用中,執行緒都不是被我們自己管理的,所以,最佳的做法就是使用ThreadLocal。一個標準的做法如下:

 

       最後作為補充,我們再來看看hibernate中ThreadLocal的應用:

 

      以下是廣告時間:最課程(http://zuikc.com)正在招收Java就業班學員,如果你想學習更多的Java高質量程式碼編寫方面的技巧,請聯絡我們哦。

相關文章