OMG,到底在尋找什么..................
          (構(gòu)造一個(gè)完美的J2EE系統(tǒng)所需要的完整知識(shí)體系)
          posts - 198,  comments - 37,  trackbacks - 0
          原貼地址:http://www.cn-java.com/target/news.php?news_id=3343

          早在Java 1.2推出之時(shí),Java平臺(tái)中就引入了一個(gè)新的支持:java.lang.ThreadLocal,給我們?cè)诰帉?xiě)多線程程序時(shí)提供了一種新的選擇。使用這個(gè)工具類可以很簡(jiǎn)潔地編寫(xiě)出優(yōu)美的多線程程序,雖然ThreadLocal非常有用,但是似乎現(xiàn)在了解它、使用它的朋友還不多。

          ThreadLocal是什么

          ThreadLocal并非是一個(gè)線程的本地實(shí)現(xiàn)版本,它并不是一個(gè)Thread,而是thread local variable(線程局部變量)。也許把它命名為T(mén)hreadLocalVar更加合適。線程局部變量(ThreadLocal)其實(shí)的功用非常簡(jiǎn)單,就是為每一個(gè)使用該變量的線程都提供一個(gè)變量值的副本,是每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)和其它線程的副本沖突。從線程的角度看,就好像每一個(gè)線程都完全擁有該變量。線程局部變量并不是Java的新發(fā)明,在其它的一些語(yǔ)言編譯器實(shí)現(xiàn)(如IBM XL FORTRAN)中,它在語(yǔ)言的層次提供了直接的支持。因?yàn)镴ava中沒(méi)有提供在語(yǔ)言層次的直接支持,而是提供了一個(gè)ThreadLocal的類來(lái)提供支持,所以,在Java中編寫(xiě)線程局部變量的代碼相對(duì)比較笨拙,這也許是線程局部變量沒(méi)有在Java中得到很好的普及的一個(gè)原因吧。

          ThreadLocal的設(shè)計(jì)

          首先看看ThreadLocal的接口:

          Object get() ;

          // 返回當(dāng)前線程的線程局部變量副本 protected Object initialValue(); // 返回該線程局部變量的當(dāng)前線程的初始值

          void set(Object value);

          // 設(shè)置當(dāng)前線程的線程局部變量副本的值

          ThreadLocal有3個(gè)方法,其中值得注意的是initialValue(),該方法是一個(gè)protected的方法,顯然是為了子類重寫(xiě)而特意實(shí)現(xiàn)的。該方法返回當(dāng)前線程在該線程局部變量的初始值,這個(gè)方法是一個(gè)延遲調(diào)用方法,在一個(gè)線程第1次調(diào)用get()或者set(Object)時(shí)才執(zhí)行,并且僅執(zhí)行1次。ThreadLocal中的確實(shí)實(shí)現(xiàn)直接返回一個(gè)null:

          protected Object initialValue() { return null; }

          ThreadLocal是如何做到為每一個(gè)線程維護(hù)變量的副本的呢?其實(shí)實(shí)現(xiàn)的思路很簡(jiǎn)單,在ThreadLocal類中有一個(gè)Map,用于存儲(chǔ)每一個(gè)線程的變量的副本。比如下面的示例實(shí)現(xiàn):


          public class ThreadLocal

          {

          private Map values = Collections.synchronizedMap(new HashMap());

          public Object get()

          {

          Thread curThread = Thread.currentThread();

          Object o = values.get(curThread);

          if (o == null && !values.containsKey(curThread))

          {

          o = initialValue();

          values.put(curThread, o);

          }

          return o;

          }


          public void set(Object newValue)

          {

          values.put(Thread.currentThread(), newValue);

          }


          public Object initialValue()

          {

          return null;

          }

          }




          當(dāng)然,這并不是一個(gè)工業(yè)強(qiáng)度的實(shí)現(xiàn),但JDK中的ThreadLocal的實(shí)現(xiàn)總體思路也類似于此。

          ThreadLocal的使用

          如果希望線程局部變量初始化其它值,那么需要自己實(shí)現(xiàn)ThreadLocal的子類并重寫(xiě)該方法,通常使用一個(gè)內(nèi)部匿名類對(duì)ThreadLocal進(jìn)行子類化,比如下面的例子,SerialNum類為每一個(gè)類分配一個(gè)序號(hào)


          public class SerialNum

          {

          // The next serial number to be assigned


          private static int nextSerialNum = 0;

          private static ThreadLocal serialNum = new ThreadLocal()

          {

          protected synchronized Object initialValue()

          {

          return new Integer(nextSerialNum++);

          }

          };


          public static int get()

          {

          return ((Integer) (serialNum.get())).intValue();

          }

          }




          SerialNum類的使用將非常地簡(jiǎn)單,因?yàn)間et()方法是static的,所以在需要獲取當(dāng)前線程的序號(hào)時(shí),簡(jiǎn)單地調(diào)用:

          int serial = SerialNum.get();

          即可。

          在線程是活動(dòng)的并且ThreadLocal對(duì)象是可訪問(wèn)的時(shí),該線程就持有一個(gè)到該線程局部變量副本的隱含引用,當(dāng)該線程運(yùn)行結(jié)束后,該線程擁有的所以線程局部變量的副本都將失效,并等待垃圾收集器收集。

          ThreadLocal與其它同步機(jī)制的比較

          ThreadLocal和其它同步機(jī)制相比有什么優(yōu)勢(shì)呢?ThreadLocal和其它所有的同步機(jī)制都是為了解決多線程中的對(duì)同一變量的訪問(wèn)沖突,在普通的同步機(jī)制中,是通過(guò)對(duì)象加鎖來(lái)實(shí)現(xiàn)多個(gè)線程對(duì)同一變量的安全訪問(wèn)的。這時(shí)該變量是多個(gè)線程共享的,使用這種同步機(jī)制需要很細(xì)致地分析在什么時(shí)候?qū)ψ兞窟M(jìn)行讀寫(xiě),什么時(shí)候需要鎖定某個(gè)對(duì)象,什么時(shí)候釋放該對(duì)象的鎖等等很多。所有這些都是因?yàn)槎鄠€(gè)線程共享了資源造成的。ThreadLocal就從另一個(gè)角度來(lái)解決多線程的并發(fā)訪問(wèn),ThreadLocal會(huì)為每一個(gè)線程維護(hù)一個(gè)和該線程綁定的變量的副本,從而隔離了多個(gè)線程的數(shù)據(jù),每一個(gè)線程都擁有自己的變量副本,從而也就沒(méi)有必要對(duì)該變量進(jìn)行同步了。ThreadLocal提供了線程安全的共享對(duì)象,在編寫(xiě)多線程代碼時(shí),可以把不安全的整個(gè)變量封裝進(jìn)ThreadLocal,或者把該對(duì)象的特定于線程的狀態(tài)封裝進(jìn)ThreadLocal。

          由于ThreadLocal中可以持有任何類型的對(duì)象,所以使用ThreadLocal get當(dāng)前線程的值是需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換。但隨著新的Java版本(1.5)將模版的引入,新的支持模版參數(shù)的ThreadLocal類將從中受益。也可以減少?gòu)?qiáng)制類型轉(zhuǎn)換,并將一些錯(cuò)誤檢查提前到了編譯期,將一定程度地簡(jiǎn)化ThreadLocal的使用。

          總結(jié)

          當(dāng)然ThreadLocal并不能替代同步機(jī)制,兩者面向的問(wèn)題領(lǐng)域不同。同步機(jī)制是為了同步多個(gè)線程對(duì)相同資源的并發(fā)訪問(wèn),是為了多個(gè)線程之間進(jìn)行通信的有效方式;而ThreadLocal是隔離多個(gè)線程的數(shù)據(jù)共享,從根本上就不在多個(gè)線程之間共享資源(變量),這樣當(dāng)然不需要對(duì)多個(gè)線程進(jìn)行同步了。所以,如果你需要進(jìn)行多個(gè)線程之間進(jìn)行通信,則使用同步機(jī)制;如果需要隔離多個(gè)線程之間的共享沖突,可以使用ThreadLocal,這將極大地簡(jiǎn)化你的程序,使程序更加易讀、簡(jiǎn)潔
          posted on 2006-10-16 12:01 OMG 閱讀(245) 評(píng)論(0)  編輯  收藏 所屬分類: J2EE

          <2006年10月>
          24252627282930
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          IT風(fēng)云人物

          文檔

          朋友

          相冊(cè)

          經(jīng)典網(wǎng)站

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 龙山县| 且末县| 乌拉特前旗| 张北县| 五原县| 郓城县| 婺源县| 灌南县| 宝丰县| 盐亭县| 赣州市| 莱芜市| 杭锦后旗| 拜泉县| 敦煌市| 红安县| 宁夏| 壤塘县| 东安县| 馆陶县| 淅川县| 渭源县| 普兰店市| 安龙县| 治多县| 山阴县| 大余县| 江门市| 赫章县| 玛曲县| 财经| 天祝| 和政县| 波密县| 甘德县| 富民县| 临清市| 嘉兴市| 和顺县| 太白县| 政和县|