?? 輕松使用線程:不共享有時是最好的.htm
字號:
4 所示的 <CODE>DebugLogger</CODE>
類作為線程局部容器來累積調試信息。在一個工作單元的開頭,您清空容器,而當一個錯誤出現時,您查詢該容器以檢索這個工作單元迄今為止生成的所有調試信息。</P>
<P><A name=listing4><B>清單 4. 用 ThreadLocal 管理每線程調試日志</B></A>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc
border=1><TBODY>
<TR>
<TD><PRE><CODE>
public class DebugLogger {
private static class ThreadLocalList extends ThreadLocal {
public Object initialValue() {
return new ArrayList();
}
public List getList() {
return (List) super.get();
}
}
private ThreadLocalList list = new ThreadLocalList();
private static String[] stringArray = new String[0];
public void clear() {
list.getList().clear();
}
public void put(String text) {
list.getList().add(text);
}
public String[] get() {
return list.getList().toArray(stringArray);
}
}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P></P>
<P>在您的代碼中,您可以調用 <CODE>DebugLogger.put()</CODE>
來保存您的程序正在做什么的信息,而且,稍后如果有必要(例如發生了一個錯誤),您能夠容易地檢索與某個特定線程相關的調試信息。
與簡單地把所有信息轉儲到一個日志文件,然后努力找出哪個日志記錄來自哪個線程(還要擔心線程爭用日志紀錄對象)相比,這種技術簡便得多,也有效得多。</P>
<P><CODE>ThreadLocal</CODE> 在基于 servlet
的應用程序或工作單元是一個整體請求的任何多線程應用程序服務器中也是很有用的,因為在處理請求的整個過程中將要用到單個線程。您可以通過前面講述的每線程單子技術用
<CODE>ThreadLocal</CODE> 變量來存儲各種每請求(per-request)上下文信息。</P>
<P><A name=4></A><A name=h14240><SPAN class=atitle2>ThreadLocal
的線程安全性稍差的堂兄弟,InheritableThreadLocal</SPAN></A><BR>ThreadLocal
類有一個親戚,InheritableThreadLocal,它以相似的方式工作,但適用于種類完全不同的應用程序。創建一個線程時如果保存了所有
<CODE>InheritableThreadLocal</CODE> 對象的值,那么這些值也將自動傳遞給子線程。如果一個子線程調用
<CODE>InheritableThreadLocal</CODE> 的
<CODE>get()</CODE>,那么它將與它的父線程看到同一個對象。為保護線程安全性,您應該只對不可變對象(一旦創建,其狀態就永遠不會被改變的對象)使用
<CODE>InheritableThreadLocal</CODE>,因為對象被多個線程共享。<CODE>InheritableThreadLocal</CODE>
很合適用于把數據從父線程傳到子線程,例如用戶標識(user id)或事務標識(transaction id),但不能是有狀態對象,例如 JDBC
<CODE>Connection</CODE>。</P>
<P><A name=5></A><A name=h15292><SPAN class=atitle2>ThreadLocal
的性能</SPAN></A><BR>雖然線程局部變量早已赫赫有名并被包括 Posix <CODE>pthreads</CODE>
規范在內的很多線程框架支持,但最初的 Java 線程設計中卻省略了它,只是在 Java 平臺的版本 1.2
中才添加上去。在很多方面,<CODE>ThreadLocal</CODE> 仍在發展之中;在版本 1.3 中它被重寫,版本 1.4
中又重寫了一次,兩次都專門是為了性能問題。</P>
<P>在 JDK 1.2 中,<CODE>ThreadLocal</CODE> 的實現方式與清單 2 中的方式非常相似,除了用同步
<CODE>WeakHashMap</CODE> 代替 <CODE>HashMap</CODE> 來存儲 values
之外。(以一些額外的性能開銷為代價,使用 WeakHashMap 解決了無法對 Thread
對象進行垃圾回收的問題。)不用說,<CODE>ThreadLocal</CODE> 的性能是相當差的。</P>
<P>Java 平臺版本 1.3 提供的 <CODE>ThreadLocal</CODE>
版本已經盡量更好了;它不使用任何同步,從而不存在可伸縮性問題,而且它也不使用弱引用。相反地,人們通過給 <CODE>Thread</CODE>
添加一個實例變量(該變量用于保存當前線程的從線程局部變量到它的值的映射的 <CODE>HashMap</CODE>)來修改
<CODE>Thread</CODE> 類以支持
<CODE>ThreadLocal</CODE>。因為檢索或設置一個線程局部變量的過程不涉及對可能被另一個線程讀寫的數據的讀寫操作,所以您可以不用任何同步就實現
<CODE>ThreadLocal.get()</CODE> 和 <CODE>set()</CODE>。而且,因為每線程值的引用被存儲在自已的
<CODE>Thread</CODE> 對象中,所以當對 <CODE>Thread</CODE> 進行垃圾回收時,也能對該
<CODE>Thread</CODE> 的每線程值進行垃圾回收。</P>
<P>不幸的是,即使有了這些改進,Java 1.3 中的 <CODE>ThreadLocal</CODE>
的性能仍然出奇地慢。據我的粗略測量,在雙處理器 Linux 系統上的 Sun 1.3 JDK 中進行
<CODE>ThreadLocal.get()</CODE> 操作,所耗費的時間大約是無爭用同步的兩倍。性能這么差的原因是
<CODE>Thread.currentThread()</CODE> 方法的花費非常大,占了
<CODE>ThreadLocal.get()</CODE> 運行時間的三分之二還多。雖然有這些缺點,JDK 1.3
<CODE>ThreadLocal.get()</CODE>
仍然比爭用同步快得多,所以如果在任何存在嚴重爭用的地方(可能是有非常多的線程,或者同步塊被頻繁地執行,或者同步塊很大),<CODE>ThreadLocal</CODE>
可能仍然要高效得多。</P>
<P>在 Java 平臺的最新版本,即版本 1.4b2 中,<CODE>ThreadLocal</CODE> 和
<CODE>Thread.currentThread()</CODE>
的性能都有了很大提高。有了這些提高,<CODE>ThreadLocal</CODE>
應該比其它技術,如用池,更快。由于它比其它技術更簡單,也更不易出錯,人們最終將發現它是避免線程間出現不希望的交互的有效途徑。</P>
<P><A name=6></A><A name=h18690><SPAN class=atitle2>ThreadLocal
的好處</SPAN></A><BR><CODE>ThreadLocal</CODE>
能帶來很多好處。它常常是把有狀態類描繪成線程安全的,或者封裝非線程安全類以使它們能夠在多線程環境中安全地使用的最容易的方式。使用
<CODE>ThreadLocal</CODE>
使我們可以繞過為實現線程安全而對何時需要同步進行判斷的復雜過程,而且因為它不需要任何同步,所以也改善了可伸縮性。除簡單之外,用
<CODE>ThreadLocal</CODE> 存儲每線程單子或每線程上下文信息在歸檔方面還有一個頗有價值好處 — 通過使用
<CODE>ThreadLocal</CODE>,存儲在 <CODE>ThreadLocal</CODE>
中的對象都是<I>不</I>被線程共享的是清晰的,從而簡化了判斷一個類是否線程安全的工作。</P>
<P>我希望您從這個系列中得到了樂趣,也學到了知識,我也鼓勵您到我的<A
href="http://www-105.ibm.com/developerWorks/java_df.nsf/AllViewTemplate?OpenForm&RestrictToCategory=23">討論論壇</A>中來深入研究多線程問題。</P>
<P><A name=resources><SPAN class=atitle2>參考資料</SPAN></A>
<UL>
<LI>參加本文的<A href="javascript:void%20forumWindow()">討論論壇</A>。<BR><BR>
<LI>這個系列的第 1 部分(<A
href="http://www-900.ibm.com/developerWorks/cn/java/j-threads/index1.shtml">同步不是敵人</A>,developerWorks,2001
年 7 月)講述非爭用同步對性能的影響如何沒有普遍相信的那么嚴重;第 2 部分(<A
href="http://www-106.ibm.com/developerWorks/java/library/j-threads2.html">減少爭用</A>,developerWorks,2001
年 9 月)講述能夠降低爭用同步對程序性能的影響的技術。<BR><BR>
<LI>Jack Shirazi 的 <A
href="http://www.amazon.com/exec/obidos/ASIN/0596000154/none0b69"><I>Java
Performance Tuning</I></A>(O'Reilly & Associates,2000)就根除 Java
平臺中性能方面的問題提供了指導。<BR><BR>
<LI>Steve Wilson 和 Jeff Kesselman 的 <A
href="http://www.amazon.com/exec/obidos/ASIN/0201709694/none0b69"><I>Java
Platform Performance: Strategies and
Tactics</I></A>(Addison-Wesley,2000)為經驗豐富的 Java
程序員提供了構建快速、高效代碼的技術。<BR><BR>
<LI>Dov Bulka 的 <A
href="http://www.amazon.com/exec/obidos/ASIN/0201704293/none0b69"><I>Java
Performance and Scalability, Volume 1: Server-Side Programming
Techniques</I></A>(Addison-Wesley,2000)在提高您應用程序的性能方面提供了豐富的技巧和竅門。<BR><BR>
<LI>Brian Goetz 的文章 <A
href="http://www.javaworld.com/jw-02-2001/jw-0209-double.html">Double-checked
locking: Clever, but broken</A>(JavaWorld,2001 年 2 月)詳細探討了 Java
內存模型(Java Memory Model,JMM)以及在特定情況下同步失敗導致的可怕后果。<BR><BR>
<LI>Doug Lea 的 <A
href="http://www.amazon.com/exec/obidos/ASIN/0201310090/none0b69"><I>Concurrent
Programming in Java, Second Edition</I></A>(Addison-Wesley,1999)是關于與
Java 多線程編程方面有關的微妙問題的一本權威書籍。<BR><BR>
<LI>Alex Roetter 在其文章<A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml">編寫多線程
Java 應用程序</A>中(developerWorks,2001 年 2 月)介紹了 Java Thread
API,略述了多線程的有關問題,并為常見問題提供了解決方案。<BR><BR>
<LI>Siva Visverwaran 的<A
href="http://www-900.ibm.com/developerWorks/cn/java/j-pool/index.shtml">連接池</A>(developerWorks,2000
年 10 月)著重討論為 J2EE 環境中數據庫資源和非數據庫資源的建立連接池提供支持的問題。<BR><BR>
<LI>作為 IBM 紅皮書的一部分的這篇文章描述了 <A
href="http://www-900.ibm.com/developerWorks/cn/cgi-bin/click.cgi?url=http://www7.software.ibm.com/vad.nsf/Data/Document2351?OpenDocument&p=1&BCT=3&Footer=1&origin=j">WebSphere
中的樂觀鎖定</A>如何讓不同的事務并發地讀取同一個狀態,如何只在更新的時候校驗數據的完整性。<BR><BR>
<LI>IBM Thomas J. Watson Research Center<A
href="http://www-900.ibm.com/developerWorks/cn/cgi-bin/click.cgi?url=http://researchweb.watson.ibm.com/compsci/performance/&origin=j">性能模型和分析</A>小組正在研究性能和性能管理領域的幾項工程。<BR><BR>
<LI>在 <A
href="http://www-900.ibm.com/developerWorks/cn/java/index.shtml">developerWorks
Java 技術專區</A>查找更多的 Java 參考資料。<BR><BR></LI></UL>
<P></P>
<P></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><A name=author1></A><SPAN class=atitle2>關于作者</SPAN><BR>Brian
Goetz 是一名軟件顧問,過去 15 年來一直是專業軟件開發者。他是 <A
href="http://www.quiotix.com/">Quiotix</A>
的首席顧問,該公司從事軟件開發和咨詢業務,位于加利福尼亞的 Los Altos。敬請查看 Brian 在流行的業界出版物中<A
href="http://www.quiotix.com/~brian/pubs.html">已發表和即將發表的論文</A>列表。可通過
<A href="mailto:brian@quiotix.com">brian@quiotix.com</A> 與 Brian 聯系。
</TD></TR></TBODY></TABLE></TD>
<TD width=10><IMG height=1 alt="" src="輕松使用線程:不共享有時是最好的.files/c.gif"
width=10 border=0></TD></TR></TBODY></TABLE><!-- END PAPER BODY--><BR
clear=all><IMG height=10 alt="" src="輕松使用線程:不共享有時是最好的.files/c.gif" width=100
border=0><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD align=right width="100%"><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-threads/index3.shtml#top">到頁首</A></TD>
<TD width=5><IMG height=1 alt="" src="輕松使用線程:不共享有時是最好的.files/c.gif"
width=5 border=0></TD></TR>
<TR vAlign=top>
<TD bgColor=#000000 colSpan=2><IMG height=1 alt=""
src="輕松使用線程:不共享有時是最好的.files/c.gif" width=100 border=0></TD></TR>
<TR vAlign=top>
<TD bgColor=#ffffff colSpan=2><IMG height=8 alt=""
src="輕松使用線程:不共享有時是最好的.files/c.gif" width=100 border=0></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD>
<FORM action=/developerWorks/cn/cnratings.nsf/RateArticle?CreateDocument
method=post><INPUT type=hidden
value="Threading lightly: Sometimes it's best not to share"
name=ArticleTitle> <INPUT type=hidden value=Java name=Zone> <INPUT
type=hidden value=/developerWorks/cn/thankyou/feedback-java.html
name=RedirectURL> <A name=rating><B>您對這篇文章的看法如何?</B></A>
<TABLE cellSpacing=0 cellPadding=0 width=600 border=0>
<TBODY>
<TR>
<TD colSpan=5><IMG height=8 alt=""
src="輕松使用線程:不共享有時是最好的.files/c.gif" width=100 border=0></TD></TR>
<TR vAlign=top>
<TD width="16%"><INPUT type=radio value=5 name=Rating>真棒!(5)</TD>
<TD width="20%"><INPUT type=radio value=4 name=Rating>好材料 (4)</TD>
<TD width="24%"><INPUT type=radio value=3 name=Rating>一般;尚可 (3)</TD>
<TD width="22%"><INPUT type=radio value=2 name=Rating>需提高 (2)</TD>
<TD width="18%"><INPUT type=radio value=1 name=Rating>太差!
(1)</TD></TR></TBODY></TABLE><BR><B>建議?</B><BR><TEXTAREA name=Comments rows=5 wrap=virtual cols=60></TEXTAREA><BR><BR><INPUT type=submit value=提交反饋意見></FORM></TD></TR>
<TR vAlign=top>
<TD bgColor=#ffffff><IMG height=8 alt=""
src="輕松使用線程:不共享有時是最好的.files/c.gif" width=100 border=0></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD align=right>(c) Copyright IBM Corp. 2001, (c) Copyright IBM China
2001, All Right Reserved</TD></TR>
<TR vAlign=top>
<TD class=bbg height=21> <A class=mainlink
href="http://www-900.ibm.com/developerWorks/cn/cgi-bin/click.cgi?url=www-900.ibm.com/cn/ibm/index.shtml&origin=dwhead">關于
IBM</A><SPAN class=divider> | </SPAN><A
class=mainlink
href="http://www-900.ibm.com/developerWorks/cn/cgi-bin/click.cgi?url=www-900.ibm.com/cn/ibm/privacy/index.shtml&origin=dwhead">隱私條約</A><SPAN
class=divider> | </SPAN><A class=mainlink
href="http://www-900.ibm.com/developerWorks/cn/cgi-bin/click.cgi?url=www-900.ibm.com/cn/ibm/legal/index.shtml&origin=dwhead">使用條款</A><SPAN
class=divider> | </SPAN><A class=mainlink
href="http://www-900.ibm.com/developerWorks/cn/cgi-bin/click.cgi?url=www-900.ibm.com/cn/ibm/contact/index.shtml&origin=dwhead">聯系
IBM</A></TD></TR></TBODY></TABLE>
<SCRIPT language=JavaScript1.2 src="輕松使用線程:不共享有時是最好的.files/stats.js"
type=text/javascript></SCRIPT>
<NOSCRIPT><IMG height=1 alt=""
src="D:\專業\JAVA\J2SE\輕松使用線程:不共享有時是最好的.files\c(1).gif" width=1
border=0></NOSCRIPT> </A></BODY></HTML>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -