ThreadLocal浅析

来源:互联网 发布:java求二进制方法 编辑:程序博客网 时间:2024/05/16 10:41

1.目的

ThreadLocal目的是保存一些线程级别的全局变量,比如connection,或者事务上下文,避免这些值需要一直通过函数参数的方式一路传递。

2. 常见用法

[java] view plaincopy
  1. public class Test2 {  
  2.     public static void main(String[] args) throws InterruptedException {  
  3.         testThreadLocal();    
  4.     }  
  5.   
  6.     private static void testThreadLocal() {  
  7.         Util.setGlobalName("zili.dengzl");  
  8.         new Foo().printName();  
  9.     }  
  10. }  
  11.   
  12. class Foo{  
  13.     public void printName(){  
  14.         System.out.println("globalName="+Util.getGlobalName());  
  15.     }  
  16. }  
  17.   
  18. class Util {  
  19.     private static final ThreadLocal<String> globalName = new ThreadLocal<String>();  
  20.   
  21.     public static String getGlobalName() {  
  22.         return globalName.get();  
  23.     }  
  24.   
  25.     public static void setGlobalName(String name) {  
  26.         globalName.set(name);  
  27.     }  
  28. }  


3.实现分析

要实现上面这样的功能,最简单的想法是用一个Map<Thread,T>,如下:

[java] view plaincopy
  1. class MockThreadLocal<T> {  
  2.     private Map<Thread, T> map = new HashMap<Thread, T>();  
  3.   
  4.     public T get() {  
  5.         return (T) map.get(Thread.currentThread());  
  6.     }  
  7.   
  8.     public void set(T value) {  
  9.         map.put(Thread.currentThread(), value);  
  10.     }  
  11. }  

这样也能实现ThreadLocal的效果,但是有一个问题,当对应的线程消失后,map中对应的线程值并不会被回收,从而造成内存泄露。

 

事实上ThreadLocal是这样做的:

[java] view plaincopy
  1. /** 
  2.  * Returns the value in the current thread's copy of this 
  3.  * thread-local variable.  If the variable has no value for the 
  4.  * current thread, it is first initialized to the value returned 
  5.  * by an invocation of the {@link #initialValue} method. 
  6.  * 
  7.  * @return the current thread's value of this thread-local 
  8.  */  
  9. public T get() {  
  10.     Thread t = Thread.currentThread();  
  11.     ThreadLocalMap map = getMap(t);  
  12.     if (map != null) {  
  13.         ThreadLocalMap.Entry e = map.getEntry(this);  
  14.         if (e != null)  
  15.             return (T)e.value;  
  16.     }  
  17.     return setInitialValue();  
  18. }  


注意这里如果取到没有该线程对应的值,会调用setInitialValue();,最终调用initialValue()生成一个值,这也是我们很多场景下要override这个方法的原因;

 

下面看一下getMap(Thread t)方法:

[java] view plaincopy
  1. ThreadLocalMap getMap(Thread t) {  
  2.     return t.threadLocals;  
  3. }  


在Thread类中:

[java] view plaincopy
  1. /* ThreadLocal values pertaining to this thread. This map is maintained 
  2.  * by the ThreadLocal class. */  
  3. ThreadLocal.ThreadLocalMap threadLocals = null;  

由此可见,所有的ThreadLocal的信息,最终是关联到Thread上的,线程消失后,对应的Thread对象也被回收,这时对应的ThreadLocal对象也会被回收。

这里为什么是一个ThreadLocalMap呢,因为一个线程可以有多个ThreadLocal变量,通过map.getEntry(this)取得对应的某个具体的变量。

[java] view plaincopy
  1. private Entry getEntry(ThreadLocal key) {  
  2.     int i = key.threadLocalHashCode & (table.length - 1);  
  3.     Entry e = table[i];  
  4.     if (e != null && e.get() == key)  
  5.         return e;  
  6.     else  
  7.         return getEntryAfterMiss(key, i, e);  
  8. }  


最后要注意的一点是,ThreadLocalMap的Entry是一个weakReference:

[java] view plaincopy
  1. /** 
  2.   * The entries in this hash map extend WeakReference, using 
  3.   * its main ref field as the key (which is always a 
  4.   * ThreadLocal object).  Note that null keys (i.e. entry.get() 
  5.   * == null) mean that the key is no longer referenced, so the 
  6.   * entry can be expunged from table.  Such entries are referred to 
  7.   * as "stale entries" in the code that follows. 
  8.   */  
  9.  static class Entry extends WeakReference<ThreadLocal> {  
  10.      /** The value associated with this ThreadLocal. */  
  11.      Object value;  
  12.   
  13.      Entry(ThreadLocal k, Object v) {  
  14.          super(k);  
  15.          value = v;  
  16.      }  
  17.  }  


这里主要因为ThreadLocalMap的key是ThreadLocal对象,如果某个ThreadLocal对象所有的强引用没有了,不能因为ThreadLocalMap的引用导致他不能被回收。

 

 

附:

这里补充一下weakReference的用法供参考(当强引用不存在时,下次垃圾回收会回收弱引用所引用的对象):

[java] view plaincopy
  1. Object o = new Object();  
  2. WeakReference<Object> ref = new WeakReference<Object>(o);  
  3. System.out.println(ref.get());  
  4. o=null;  
  5. System.gc();  
  6. System.out.println(ref.get());  
  7.       

结果输出: 

[plain] view plaincopy
  1. java.lang.Object@de6ced  
  2. null  

 


0 0
原创粉丝点击