📄 轻松使用线程:不共享有时是最好的.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 + -