ThreadLocal是什么ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明,在其它的一些语言编译器实现(如IBMXLFORTRAN)中,它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线程局部变量的代码相对比较笨拙,这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。ThreadLocal的设计首先看看ThreadLocal的接口:Objectget();//返回当前线程的线程局部变量副本protectedObjectinitialValue();//返回该线程局部变量的当前线程的初始值voidset(Objectvalue);//设置当前线程的线程局部变量副本的值ThreadLocal有3个方法,其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()或者set(Object)时才执行,并且仅执行1次。ThreadLocal中的确实实现直接返回一个null:protectedObjectinitialValue(){returnnull;}ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现:publicclassThreadLocal{privateMapvalues=Collections.synchronizedMap(newHashMap());publicObjectget(){ThreadcurThread=Thread.currentThread();Objecto=values.get(curThread);if(o==null&&!values.containsKey(curThread)){o=initialValue();values.put(curThread,o);}returno;}publicvoidset(ObjectnewValue){values.put(Thread.currentThread(),newValue);}publicObjectinitialValue(){returnnull;}}当然,这并不是一个工业强度的实现,但JDK中的ThreadLocal的实现总体思路也类似于此。ThreadLocal的使用如果希望线程局部变量初始化其它值,那么需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行子类化,比如下面的例子,SerialNum类为每一个类分配一个序号:publicclassSerialNum{//ThenextserialnumbertobeassignedprivatestaticintnextSerialNum=0;privatestaticThreadLocalserialNum=newThreadLocal(){protectedsynchronizedObjectinitialValue(){returnnewInteger(nextSerialNum++);}};publicstaticintget(){return((Integer)(serialNum.get())).intValue();}}SerialNum类的使用将非常地简单,因为get()方法是static的,所以在需要获取当前线程的序号时,简单地调用:intserial=SerialNum.get();即可。在线程是活动的并且ThreadLocal对象是可访问的时,该线程就持有一个到该线程局部变量副本的隐含引用,当该线程运行结束后,该线程拥有的所以线程局部变量的副本都将失效,并等待垃圾收集器收集。Thread-SpecificStoragepattern假设有一个如下的interface,当我呼叫完call()以后,想知道是否有发生error,或者发生什么error,那么就用errno()方法来调查.(这个前提听起来有一点笨,但是在实际的系统中却常存在)LegacySystem.javainterfaceLegacySystem{publicvoidcall(intparameter);publicinterrno();}在这边我们实现了上面的interface,为了简单一点,在这里不做任何复杂动作,只单纯把parameter传给errnoclassLegacySystemImplimplementsLegacySystem{privateinterrno;publicvoidcall(intparameter){errno=parameter;}publicinterrno(){returnerrno;}}上面的code很简单只是传入参数,还有印出的动作。如果在singlethread下,上面的class不会有任何问题,但是当LegacySystemImpl物件被复数的thread存取,那么将会发生问题,也就是说其他的thread呼叫call会影响到自己呼叫errno()方法的结果。主要的如下现在两个thread去呼叫,第一个thread一直去呼叫call(0),然后5秒以后第二个thread一直呼叫call(1),如果发生传入call的值和errno()方法回转的值不同时,印出???并呼叫System.exit结束。Main1.javaclassMain1extendsThread{privatestaticLegacySystemsystem=newLegacySystemImpl();privateintvalue=0;publicMain1(intvalue){this.value=value;}publicvoidrun(){while(true){System.out.println(Thread.currentThread().getName()+checks.);system.call(value);try{Thread.sleep(100);}catch(InterruptedExceptione){}interrno=system.errno();System.out.println(Thread.currentThread().getName()+:value=+value+,errno=+errno);if(value!=errno){System.out.println(???);System.exit(0);}}}publicstaticvoidmain(String[]args){newMain1(0).start();try{Thread.sleep(5000);}catch(InterruptedExceptione){}newMain1(1).start();}}结果如下Thread-0checks.Thread-0:value=0,errno=0Thread-0checks.Thread-0:value=0,errno=0Thread-0checks.Thread-0:value=0,errno=0(中略)Thread-0checks.Thread-0:value=0,errno=0Thread-0checks.Thread-1checks.←Thread-1启动以后…Thread-0:value=0,errno=1←结果乱七八糟。???所以现在改写成如下的proxyLegacySystemProxy.javaclassLegacySystemProxyimplementsLegacySystem{privateThreadLocalthlocal=newThreadLocal();publicvoidcall(intparameter){getImpl().call(parameter);}publicinterrno(){returngetImpl().errno();}privateLegacySystemImplgetImpl(){LegacySystemImplimpl=(LegacySystemImpl)thlocal.get();if(impl==null){impl=newLegacySystemImpl();thlocal.set(impl);}returnimpl;}}在这里使用的java.lang.ThreadLocal,让现在的thread保有其固有的领域(对thread来说specific的领域),为了让thread有固有的LegacySystemImpl,所以使用threadlocal,使用threadlocal的get和set方法可以去读现在的thread的固有领域写个新的mainMain2.javaclassMain2extendsThread{privatestaticLegacySystemsystem=newLegacySystemProxy();privateintvalue=0;publicMain2(intvalue){this.value=value;}publicvoidrun(){while(true){System.out.println(Thread.currentThread().getName()+checks.);system.call(value);try{Thread.sleep(100);}catch(InterruptedExceptione){}interrno=system.errno();System.out.println(Thread.currentThread().getName()+:value=+value+,errno=+errno);if(value!=errno){System.out.println(???);System.exit(0);}}}publicstaticvoidmain(String[]args){newMain2(0).start();try{Thread.sleep(5000);}catch(InterruptedExceptione){}newMain2(1).start();}}结果如下Thread-0checks.Thread-0:value=0,errno=0(中略)Thread-0checks.Thread-1checks.Thread-0:value=0,errno=0Thread-0checks.Thread-1:value=1,errno=1Thread-1checks.Thread-0:value=0,errno=0Thread-0checks.←就像这样Thread-1checks.←就算这样也不怕。Thread-0:value=0,errno=0Thread-0checks.Thread-1:value=1,errno=1Thread-1checks.Thread-0:value=0,errno=0(后略)