ThreadLocal 应用原理解析与常见问题
ThreadLocal是大家比较常用到的,在多线程下存储线程相关数据十分合适。可是很多时候我们并没有深入去了解它的原理。
首选提出几个问题,稍后再针对这些问题一一解答。
- 提到ThreadLocal,大家常说ThreadLocal是弱引用,那么ThreadLocal究竟是如何实现弱引用的呢?
- ThreadLocal是如何做到可以当做线程局部变量的呢?
- 大家创建ThreadLocal变量时,为什么都要用static修饰?
- 大家争论不止的ThreadLocal内存泄漏是什么鬼?
进入正题,先简单了解下ThreadLocal 和 Thread,ThreadLocal的类结构:
可以看到,ThreadLocal有个内部类ThreadLocalMap,ThreadLocalMap又有个内部类Entry。
Thread类有这样一段源码:
class Thread implements Runnable { ...省略若干代码 /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null; /* * InheritableThreadLocal values pertaining to this thread. This map is * maintained by the InheritableThreadLocal class. */ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
通过Thread源码我们了解到,Thread持有的对象是ThreadLocal的ThreadLocalMap,这一点特别重要,线程相关数据都是通过ThreadLocalMap存储的,而不是ThreadLocal。
此时我们得到的结论如下图所示:
Thread的threadLocals属性直接关联的ThreadLocal.ThreadLocalMap,和ThreadLocal没有丝毫关系
那么ThreadLocal是做什么的呢?其实ThreadLocal可以看做线程操作ThreadLocalMap的工具类,ThreadLocal暴漏了两个公共方法get()和set(T)用来获取和设置ThreadLocalMap。
了解一下set方法源码:
1 public void set(T value) { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) 5 map.set(this, value); 6 else7 createMap(t, value); 8 }
从源码第五行我们可以得到两个重要的信息:
- 获取ThreadLocalMap时,使用了当前Thread对象 t 作为参数。
getMap(t)方法的实现很简单:
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
它返回的是Thread的 threadLocals 属性,代码上验证了:“