聲明:本文是《?Java 7 Concurrency Cookbook?》的第一章, 作者: Javier Fernández González 譯者:鄭玉婷 校對:方騰飛
使用本地線程變量
并發應用的一個關鍵地方就是共享數據。這個對那些擴展Thread類或者實現Runnable接口的對象特別重要。
如果你創建一個類對象,實現Runnable接口,然后多個Thread對象使用同樣的Runnable對象,全部的線程都共享同樣的屬性。這意味著,如果你在一個線程里改變一個屬性,全部的線程都會受到這個改變的影響。
有時,你希望程序里的各個線程的屬性不會被共享。 Java 并發 API提供了一個很清楚的機制叫本地線程變量。
在這個指南中, 我們將開發一個程序,這個程序用來描述在第一段話里的問題,和另一個程序使用本地線程變量機制解決這個問題。
準備
指南中的例子是使用Eclipse IDE 來實現的。如果你使用Eclipse 或者其他的IDE,例如NetBeans, 打開并創建一個新的java項目。
怎么做呢…
按照這些步驟來實現下面的例子:
1.? ?首先,我們來實現一個程序含有上述的問題。
創建一個類名為 UnsafeTask 并實現 Runnable 接口。 聲明一個?private?java.util.Date 屬性.
1 | public ?class ?UnsafeTask? implements ?Runnable{ |
2 | private ?Date startDate; |
2. 實現UnsafeTask 對象的run() 方法,此方法會初始 startDate 屬性, 把值寫入控制臺,隨機休眠一段時間,最后在寫入startDate 屬性。
04 | System.out.printf( "Starting Thread: %s : %s\n" ,Thread. currentThread().getId(),startDate); |
06 | TimeUnit.SECONDS.sleep( ( int )Math.rint(Math.random()* 10 )); |
07 | }? catch ?(InterruptedException e) { |
10 | System.out.printf( "Thread Finished: %s : %s\n" ,Thread. currentThread().getId(),startDate); |
3.? ?現在,來實現這個有問題例子的主類。創建一個 Main ?類和 main() 方法. 此方法會創建一個 UnsafeTask 類的對象,并開始3個線程使用這個對象,每個線程間休眠2秒。
02 | public ?static ?void ?main(String[] args) { |
03 | UnsafeTask task= new ?UnsafeTask(); |
04 | ? for ?( int ?i= 0 ; i< 10 ; i++){ |
05 | Thread thread= new ?Thread(task); |
07 | try ?{ TimeUnit.SECONDS.sleep( 2 ); |
08 | }? catch ?(InterruptedException e) { |
4.? ?在以下的裁圖,你可以發現這個程序的執行結果。每個線程有著不同的開始時間,但是全部都有相同的結束時間。

5.? ?如在之前提到的, 我們會使用本地線程變量機制來解決這個問題。
6.? ?創建一個類名為 SafeTask a一定實現 Runnable 接口。
1 | public ?class ?SafeTask? implements ?Runnable { |
7.?? 聲明 ThreadLocal<Date> 類對象。此對象有隱含實現了 initialValue()方法. 此方法會返回真實日期。
1 | private ?static ?ThreadLocal<Date> startDate=? new ?ThreadLocal<Date>() { |
2 | protected ?Date initialValue(){ |
8.? ?實現run()方法。它和 UnsafeClass的run() 方法功能一樣,只是改變了屬性的訪問方式。
03 | ?? System.out.printf( "Starting Thread: %s : %s\n" ,Thread.currentThread().getId(),startDate.get()); |
05 | ?? TimeUnit.SECONDS.sleep(( int )Math.rint(Math.random()* 10 )); |
06 | }? catch ?(InterruptedException e) { |
09 | System.out.printf( "Thread Finished: %s : %s\n" ,Thread.currentThread().getId(),startDate.get()); |
9. ???這個例子的主類跟不安全例子一樣,把名字改成 Runnable 類。
10. 運行例子并分析不同處。
它是怎么工作的…
在下面的截圖里,你可以看到線程安全模式下程序運行的結果。現在3個 Thread 對象都有他們自己的startDate 屬性值。看下圖:

本地線程變量為每個使用這些變量的線程儲存屬性值。可以用 get() 方法讀取值和使用 set() 方法改變值。 如果第一次你訪問本地線程變量的值,如果沒有值給當前的線程對象,那么本地線程變量會調用 initialValue() 方法來設置值給線程并返回初始值。
更多…
本地線程類還提供 remove() 方法,刪除存儲在線程本地變量里的值。
Java 并發 API 包括 InheritableThreadLocal 類提供線程創建線程的值的遺傳性 。如果線程A有一個本地線程變量,然后它創建了另一個線程B,那么線程B將有與A相同的本地線程變量值。 你可以覆蓋 childValue() 方法來初始子線程的本地線程變量的值。 它接收父線程的本地線程變量作為參數。
原創文章,轉載請注明:?轉載自并發編程網 – ifeve.com本文鏈接地址:?線程管理(九)使用本地線程變量
文章轉自?并發編程網-ifeve.com