原文: ThreadLocal-分析-总结 网上虽然很多关于ThreadLocal , 但是这篇文章,从
源码角度进行剖析 ,让你不得不信服,Thanks!
以前,对ThreadLocal 错误的理解:
它内部维护了一个map,然后,这个map的key是本地线程的标志,value则是线程不安全的对象
其实,不是这样的:
1. ThreadLocal 中有个内部类,ThreadLocalMap ,但是它并没有map这个成员变量,它只是对map进行操作!
map则是存在于各个线程中的。查看Thread的源代码就可以看到每个Thread都有一个ThreadLocal的成员变量!
2. ThreadLocal中的key不是线程的标志,而是TheadLocal这个对象,map不是在TheadLocal中!!!
3. 每个线程中都有一个ThreadLocalMap , map的key为ThreadLocal对象,这样不管你有多少个ThreadLocal , 线程中
都会存储你相应的值!
4. 这样看的话,ThreadLocal 的作用,就会使某个线程一直占用着某个资源!并不是数据的共享!
当然,如果你set的对象是一个线程间共享的对象,那就另当别论了。比如 只要一个Node n , 而所有线程都是set的这个!
不过,这就与ThreadLocal的使用场景不同!它里面存放是不同的对象!每个线程都是不同的!
5.将map绑定到当前线程的好处:
当前线程销毁的时候,相应的map也销毁了,这里,要注意,线程池的情况!!!
各线程访问的map是各自不同的map,所以不需要同步,速度会快些;而如果把所有线程要用的对象都放到一个静态map中的话 多线程并发访问需要进行同步。
5.ThreadLocal 与 synchronized的区别:
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
当然ThreadLocal并不能替代synchronized ,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。
图片出处: http://www.cnblogs.com/onlywujun/p/3524675.html
下面是原文:
ThreadLocal<T>类在Spring,Hibernate等框架中起到了很大的作用,对于其工作原理,很多网上的文章分析的不够彻底,甚至有些误解。
首先,为了解释ThreadLocal类的工作原理,必须同时介绍与其工作甚密的其他几个类(内部类)
1.ThreadLocalMap
2.Thread
可能有人会觉得Thread与ThreadLocal有什么关系,其实真正的奥秘就在Thread类中的一行:
ThreadLocal.ThreadLocalMap threadLocals = null;
其中ThreadLocalMap的定义是在ThreadLocal类中,真正的引用却是在Thread类中
那么ThreadLocalMap究竟是什么呢?
可以看到这个类应该是一个Map,JDK的解释是
接下来的重点是ThreadLocalMap中用于存储数据的entry
static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } }
从中我们可以发现这个Map的key是ThreadLocal变量,value为用户的值,并不是网上大多数的列子key是线程的名字或者标识
到这里,我们就可以理解ThreadLocal究竟是如何工作的了
1.Thread类中有一个成员变量叫做ThreadLocalMap,它是一个Map,他的Key是ThreadLocal类
2.每个线程拥有自己的申明为ThreadLocal类型的变量,所以这个类的名字叫'ThreadLocal':线程自己的(变量)
3.此变量生命周期是由该线程决定的,开始于第一次初始(get或者set方法)
4.由ThreadLocal的工作原理决定了:每个线程独自拥有一个变量,并非共享或者拷贝
/** * @author mxdba * */ public class ThreadLocalSample { public static void main(String[] args) { ThreadTest test1 = new ThreadTest(10); ThreadTest test2 = new ThreadTest(20); test1.start(); test2.start(); } } /** * 此线程有两个ThreadLocal变量,但是由于ThreadLocal是延迟初始的, * 所以在debug时可以看到线程名为“线程20”的线程的ThreadLocalMap中没有thLcal2这个entry * @author mxdba * */ class ThreadTest extends Thread { public static ThreadLocal<Integer> thLocal = new ThreadLocal<Integer>(); public static ThreadLocal<String> thLocal2 = new ThreadLocal<String>(); public Integer num; public ThreadTest(Integer num) { super("线程" + num); this.num = num; } @Override public void run() { Integer n = thLocal.get(); if(num != 20) { String s = thLocal2.get(); } if(n == null) { thLocal.set(num); } System.out.println(thLocal.get()); } }
接下来分析一下源码,就更加清楚了
/** * 关键方法,返回当前Thread的ThreadLocalMap * [[[每个Thread返回各自的ThreadLocalMap,所以各个线程中的ThreadLocal均为独立的]]] */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
get方法源代码:
public T get() { Thread t = Thread.currentThread(); /** * 得到当前线程的ThreadLocalMap */ ThreadLocalMap map = getMap(t); if (map != null) { /** * 在此线程的ThreadLocalMap中查找key为当前ThreadLocal对象的entry */ ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }
初始化代码:
private T setInitialValue() { /** * 默认返回null,这个方法为protected可以继承 */ T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else /** * 初次创建 */ createMap(t, value); return value; }
/** * 给当前thread初始ThreadlocalMap */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
通过上边的分析,我们发现,ThreadLocal类的使用虽然是用来解决多线程的问题的,但是还是有很明显的针对性
使用场景:
1.最明显的,ThreadLoacl变量的活动范围为某线程,并且我的理解是该线程“专有的,独自霸占”,对该变量的所有操作均有该线程完成!也就是说,ThreadLocal不是用来解决共享,竞争问题的。典型的应用莫过于Spring,Hibernate等框架中对于多线程的处理了
private static final ThreadLocal threadSession = new ThreadLocal(); public static Session getSession() throws InfrastructureException { Session s = (Session) threadSession.get(); try { if (s == null) { s = getSessionFactory().openSession(); threadSession.set(s); } } catch (HibernateException ex) { throw new InfrastructureException(ex); } return s; }
这段代码,每个线程有自己的ThreadLocalMap,每个ThreadLocalMap中根据需要初始加载threadSession,这样的好处就是介于singleton与prototype之间,应用singleton无法解决线程,应用prototype开销又太大,有了ThreadLocal之后就好了,对于需要线程“霸占”的变量用ThreadLocal,而该类实例的方法均可以共享。(例如,某个成员变量,我们要独占,就可以用ThreadLocal , 这样看的话,ThreadLocal类,不就可以看成
一个可以实现独占功能类!)
试想如果不用ThreadLocal怎么来实现呢?
可能就要在action中创建session,然后把session一个个传到service和dao中,这可够麻烦的。而ThreadLocal则是将变量绑定到当前线程,只要是在这个线程中就可以访问得到。
或者可以自己定义一个静态的map,将当前thread作为key,创建的session作为值,put到map中,应该也行,这也是一般人的想法,
但事实上,ThreadLocal的实现刚好相反,它是在每个线程中有一个map,而将ThreadLocal实例
作为key,这样每个map中的项数很少,而且当线程销毁时相应的东西也一起销毁了,不知道除了这些还有什么其他的好处。
3、存放一些context变量,比如webwork的ActionContext
3.关于内存泄漏:
虽然ThreadLocalMap已经使用了weakReference,但是还是建议能够显示的使用remove方法。