﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-jclown-随笔分类-Java 杂谈</title><link>http://www.blogjava.net/faideland/category/46377.html</link><description /><language>zh-cn</language><lastBuildDate>Sun, 19 Sep 2010 15:26:15 GMT</lastBuildDate><pubDate>Sun, 19 Sep 2010 15:26:15 GMT</pubDate><ttl>60</ttl><item><title>理解 ThreadLocal</title><link>http://www.blogjava.net/faideland/archive/2010/09/19/332470.html</link><dc:creator>jclown</dc:creator><author>jclown</author><pubDate>Sun, 19 Sep 2010 12:06:00 GMT</pubDate><guid>http://www.blogjava.net/faideland/archive/2010/09/19/332470.html</guid><wfw:comment>http://www.blogjava.net/faideland/comments/332470.html</wfw:comment><comments>http://www.blogjava.net/faideland/archive/2010/09/19/332470.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faideland/comments/commentRss/332470.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faideland/services/trackbacks/332470.html</trackback:ping><description><![CDATA[<p><font size="3"><span style="font-family: 黑体"><font style="font-family: " size="3"></font></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font face="仿宋_GB2312" size="3">ThreadLocal是线程变量的意思，不是本地线程。它可以为线程分配特有的空间(与线程自身的堆栈不同，是线程对象在内存中的一个Map)，使得线程可以通过这个空间随时获取自己相关的上下文信息。</font> </p>
<h2><span style="font-size: 18pt; color: #000080">ThreadLocal 内幕 </span></h2>
<p><font face="仿宋_GB2312" size="3">ThreadLocal很容易让使用者误解(以前我就误解了)，因为在这个类中定义了一个静态的包级可见的内部类ThreadLocalMap，同时ThreadLocal对外提供了get和set方法。那么set的时候就是把当前线程对象作为ThreadLocalMap的key，把传入set方法的值作为value存储起来；get的时候通过获取当前线程对象，然后到ThreadLocalMap中取得对应的value。这种认识是表面的很容易理解的方式，但是也是错误的理解。</font>
<p><font face="仿宋_GB2312" size="3">每个Thread类中都有ThreadLocal.ThreadLocalMap的两个成员，分别是threadlocals与inheritableThreadLocals。前者用来存储当前线程的相关数据信息，后者用来存储从父线程中继承或者说拷贝过来的相关数据信息。这两个区域也就是前面提到的线程的特别存储区域，但是如何操作它们？JDK中的设计并不是让用户直接操作当前线程来存取数据，而是让用户操作ThreadLocal来对当前线程的这两个Map进行存取(个人认为这样设计是出于安全性与易用性考虑，主要是Map中的key值需要固定才能取到同一对象)，同时放置在这两个Map中的key值就是ThreadLocal对象自身，value由用户传入。</font>
<h2><span style="font-size: 18pt"><br />
<span style="color: #000080">ThreadLocal 进阶 </span></span></h2>
<p><font face="仿宋_GB2312" size="3">ThreadLocal为线程独占某些对象或者资源提供了新的手段，ThreadLocal与线程堆栈都是用来存取线程私有数据的区域，别的线程无法触碰这两个区域，但是二者确有不同的生命周期。这个可以这样理解，线程跨多个方法调用的时候，方法作为Frame不断的进栈和出栈(方法的调用与返回)，同时出栈的时候方法中的临时变量数据会被回收，在这个线程的堆栈中消失，所以方法中的局部变量是无法在线程调用过程中穿透，除非是传递局部的引用类型变量，但是这也意味着方法接口都要额外接收新的参数(早期系统中传递JDBC数据库连接的方式)。而ThreadLocal不同，准确地说是线程中的ThreadLocalMap不同，线程对象中的ThreadLocalMap对象是伴随整个线程对象生命周期的，线程可以在任何需要的时候通过它来获取相关数据，这为线程独占数据提供了一种更加优雅的方式。只不过这一切都是通过ThreadLocal来操作的而已，同时ThreadLocal自身也作为不同线程中的ThreadLocalMap中的key来映射存入的value。<br />
<br />
</font>
<h2><span style="font-size: 18pt; color: #000080">ThreadLocal 应用 </span></h2>
<p><font face="Courier New" color="#7f0055" size="2"><strong>public class</strong></font><font face="Courier New" size="2"> ThreadWriter</font>
<p><font face="Courier New" size="2">{</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp; </font><font face="Courier New" color="#7f0055" size="2"><strong>private static final</strong></font><font face="Courier New" size="2"> ThreadLocal&lt;FileWriter&gt;&nbsp;</font><font face="Courier New" color="#0000c0" size="2"><em>local</em></font><font face="Courier New" size="2">&nbsp;= </font><font face="Courier New" color="#7f0055" size="2"><strong>new</strong></font>
<p><font face="Courier New" size="2">ThreadLocal&lt;FileWriter&gt;();</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp; </font><font face="Courier New" color="#7f0055" size="2"><strong>public void</strong></font><font face="Courier New" size="2"> write(String s)</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp; {</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font face="Courier New" color="#7f0055" size="2"><strong>try</strong></font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getFileWriter().write(s);</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font face="Courier New" color="#7f0055" size="2"><strong>catch</strong></font><font face="Courier New" size="2">(IOException e)</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //igore</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp; }</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp; </font><font face="Courier New" color="#7f0055" size="2"><strong>private</strong></font><font face="Courier New" size="2"> FileWriter getFileWriter()</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp; {</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileWriter fw = </font><font face="Courier New" color="#0000c0" size="2"><em>local</em></font><font face="Courier New" size="2">.get();</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font face="Courier New" color="#7f0055" size="2"><strong>if</strong></font><font face="Courier New" size="2"> (</font><font face="Courier New" color="#7f0055" size="2"><strong>null</strong></font><font face="Courier New" size="2"> == fw)</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font face="Courier New" color="#7f0055" size="2"><strong>try</strong></font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font>
<p><font face="Courier New" size="2">fw = new FileWriter(Thread.currentThread().getName() + "-info.txt", </font><font face="Courier New" color="#7f0055" size="2"><strong>true</strong></font><font face="Courier New" size="2">);</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font face="Courier New" color="#0000c0" size="2"><em>local</em></font><font face="Courier New" size="2">.set(fw);</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font face="Courier New" color="#7f0055" size="2"><strong>catch</strong></font><font face="Courier New" size="2">(IOException e)</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//igore</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font face="Courier New" color="#7f0055" size="2"><strong>return</strong></font><font face="Courier New" size="2"> fw;</font>
<p><font face="Courier New" size="2">&nbsp;&nbsp;&nbsp; }</font>
<p><font face="Courier New" size="2">}<br />
</font>
<p><font face="仿宋_GB2312" size="3">这里说明几个问题：</font>
<p><font face="仿宋_GB2312" size="3">1、&nbsp;ThreadLocal对象只有一份，它将作为不同线程中的ThreadLocalMap对象的key，所映射的值是不同文件路径的FileWriter对象。只不过不同 线程中的ThreadLocalMap中的key值都一样，但是因为在不同的ThreadLocalMap中，不会有任何影响，反而这样的方式为每个线程中ThreadLocalMap对象中的key值生成省去了很多功夫，这是一个很精妙的设计，用户在间接使用ThreadLocalMap的时候，不需要感知key是什么，就可以方便地存取。</font>
<p><font face="仿宋_GB2312" size="3">2、&nbsp;一份ThreadLocal对象只能映射一种资源。如需要映射多种资源的话，定义多个ThreadLocal成员。</font>
<p><font face="仿宋_GB2312" size="3">3、 </font><font face="Courier New" color="#0000c0" size="2"><em>local</em></font><font face="Courier New" size="2">.set(fw); </font><font face="仿宋_GB2312" size="3">这句代码表示将local自身作为key，fw作为value，存储在调用这行代码的线程中的ThreadLocalMap对象中。</font>
<p><font face="Courier New" size="2">FileWriter fw = </font><font face="Courier New" color="#0000c0" size="2"><em>local</em></font><font face="Courier New" size="2">.get(); </font><font face="仿宋_GB2312" size="3">这句代码表示将local自身作为key，从当前线程的ThreadLocalMap对象中取出属于自己独占的FileWriter对象。</font><font face="Courier New" size="2">&nbsp;</font></p>
 <img src ="http://www.blogjava.net/faideland/aggbug/332470.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faideland/" target="_blank">jclown</a> 2010-09-19 20:06 <a href="http://www.blogjava.net/faideland/archive/2010/09/19/332470.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>