Java執行緒使用技巧學習(二)
Java執行緒使用技巧學習(二)
進階篇 3.執行緒本地儲存 這個和前面提到的兩個略有不同。ThreadLocal是在Thread類之外實現的一個功能(java.lang.ThreadLocal),但它會為每個執行緒分別儲存一份唯一的資料。正如它的名字所說的,它為執行緒提供了本地儲存,也就是說你所創建出來變數對每個執行緒例項來說都是唯一的。和執行緒名,執行緒優先順序類似,你可以自定義出一些屬性,就好像它們是儲存在Thread執行緒內部一樣,是不是覺得酷?不過先別高興得太早了,有幾句醜話得先說在前頭。 建立ThreadLocal有兩種推薦方式:要麼是靜態變數,要麼是單例例項中的屬性,這樣可以是非靜態的。注意,它的作用域是全域性的,只不過對訪問它的執行緒而言好像是本地的而已。在下面這個例子中,ThreadLocal裡面儲存了一個數據結構,這樣我們可以很容易地訪問到它:
public static class CriticalData
{
public int transactionId;
public int username;
}
public static final ThreadLocal<CriticalData> globalData =
new ThreadLocal<CriticalData>();
一旦獲取到了ThreadLocal物件,就可以通過 globalData.set()和globalData.get()方法來對它進行操作了。
全域性變數?這不是什麼好事
也盡然。ThreadLocal可以用來儲存事務ID。如果程式碼中出現未捕獲異常的時候它就相當有用了。最佳實踐是設定一個UncaughtExceptionHandler,這個是Thread類本身就支援的,但是你得自己去實現一下這個介面。一旦執行到了UncaughtExceptionHandler裡,就幾乎沒有任何線索能夠知道到底發生了什麼事情了。這會兒你能獲取到的就只有Thread物件,之前導致異常發生的所有變數都無法再訪問了,因為那些棧幀都已經被彈出了。一旦到了UncaughtExceptionHandler裡,這個執行緒就只剩下最後一口氣了,唯一能抓住的最後一根稻草就是ThreadLocal。
我們來試下這麼做:
1System.err.println("Transaction ID "
+ globalData.get().transactionId);
我們可以將一些與錯誤相關的有價值的上下文資訊給儲存到裡面添。ThreadLocal還有一個更有創意的用法,就是用它來分配一塊特定的記憶體,這樣工作執行緒可以把它當作快取來不停地使用。當然了,這有沒有用得看你在CPU和記憶體之間是怎麼權衡的了。沒錯,ThreadLocal需要注意的就是會造成記憶體空間的浪費。只要執行緒還活著,那麼它就會一直存在,除非你主動釋放否則它是不會被回收的。因此如果使用它的話你最好注意一下,儘量保持簡單。
4. 使用者執行緒及守護執行緒
我們再回到Thread類。程式中的每個執行緒都會有一個狀態,要麼是使用者狀態,要麼是守護狀態。換句話說,要麼是前臺執行緒要麼是後臺執行緒。主執行緒預設是使用者執行緒,每個新執行緒都會從建立它的執行緒中繼承執行緒狀態。因此如果你把一個執行緒設定成守護執行緒,那麼它所建立的所有執行緒都會被標記成守護執行緒。如果程式中的所有執行緒都是守護執行緒的話,那麼這個程序便會終止。我們可以通過Boolean .setDaemon(true)和.isDaemon()方法來檢視及設定執行緒狀態。
什麼時候會用到守護執行緒?taskset -c 1 “<span id="4_nwp" style="width: auto;
height: auto; float: none;"><a id="4_nwl"
href="http://cpro.baidu.com/cpro/ui/uijs.php?
c=news&cf=1001&ch=0&di=128&fv=11&jk=30cdc418629cf3dd&k=
java&k0=java&kdi0=0&luki=10&n=10&p=baidu&q=06011078_cpr
&rb=0&rs=1&seller_id=1&sid=ddf39c6218c4cd30&ssp2=1&stid=0
&t=tpclicked3_hc&tu=u1922429&u=http%3A%2F%2Fwww%2Eadmin10000
%2Ecom%2Fdocument%2F5854%2Ehtml&urlid=0" target="_blank"
mpid="4" style="text-decoration: none;">
<span style="color:#0000ff;font-size:14px;width:auto;
height:auto;float:none;">java</span></a></span> AboutToBePinned”
如果是一個已經在運行了的程序:
taskset -c 1 <PID>
要想深入到執行緒級別還得再加些程式碼才行。所幸的是,有一個開源庫能完成這樣的功能:Java-Thread-Affinity。這個庫是由OpenHFT的Peter Lawrey開發的,實現這一功能最簡單直接的方式應該就是使用這個庫了。我們通過一個例子來快速看下如何繫結某個執行緒,關於該庫的更多細節請參考它在Github上的文件:
AffinityLock al = AffinityLock.acquireLock();
這樣就可以了。關於獲取鎖的一些更高階的選項——比如說根據不同的策略來選擇CPU——在Github上都有詳細的說明。 結論 本文我們介紹了關於執行緒的5點知識:執行緒名,執行緒本地儲存,優先順序,守護執行緒以及處理器親和性。希望這能為你日常工作中所用到的內容開啟一扇新的窗戶,期待你們的反饋!還有什麼有關執行緒處理的方法可以分享給大家的嗎,請不吝賜教。