深入解析ThreadLocal( 六 )

  • 线程隔离 : 各线程之间的数据相互隔离却又具备并发性 , 避免同步方式带来的性能损失
  • 3. ThreadLocal的内部结构通过以上的学习 , 我们对ThreadLocal的作用有了一定的认识 。 现在我们一起来看一下ThreadLocal的内部结构 , 探究它能够实现线程数据隔离的原理 。
    3.1 常见的误解如果我们不去看源代码的话 , 可能会猜测ThreadLocal是这样子设计的:每个ThreadLocal都创建一个Map , 然后用线程作为Map的key , 要存储的局部变量作为Map的value , 这样就能达到各个线程的局部变量隔离的效果 。 这是最简单的设计方法 , JDK最早期的ThreadLocal 确实是这样设计的 , 但现在早已不是了 。
    3.2 现在的设计但是 , JDK后面优化了设计方案 , 在JDK8中 ThreadLocal的设计是:每个Thread维护一个ThreadLocalMap , 这个Map的key是ThreadLocal实例本身 , value才是真正要存储的值Object 。
    具体的过程是这样的:
    (1) 每个Thread线程内部都有一个Map (ThreadLocalMap) ? (2) Map里面存储ThreadLocal对象(key)和线程的变量副本(value) ? (3)Thread内部的Map是由ThreadLocal维护的 , 由ThreadLocal负责向map获取和设置线程的变量值 。? (4)对于不同的线程 , 每次获取副本值时 , 别的线程并不能获取到当前线程的副本值 , 形成了副本的隔离 , 互不干扰 。
    3.3 这样设计的好处这个设计与我们一开始说的设计刚好相反 , 这样设计有如下两个优势:
    (1) 这样设计之后每个Map存储的Entry数量就会变少 。 因为之前的存储数量由Thread的数量决定 , 现在是由ThreadLocal的数量决定 。 在实际运用当中 , 往往ThreadLocal的数量要少于Thread的数量 。
    (2) 当Thread销毁之后 , 对应的ThreadLocalMap也会随之销毁 , 能减少内存的使用 。
    4. ThreadLocal的核心方法源码基于ThreadLocal的内部结构 , 我们继续分析它的核心方法源码 , 更深入的了解其操作原理 。
    除了构造方法之外 ,ThreadLocal对外暴露的方法有以下4个:
    data-draft-node="block" data-draft-type="table" data-size="normal" data-row-style="normal">
    方法声明描述
    以下是这4个方法的详细源码分析(为了保证思路清晰, ThreadLocalMap部分暂时不展开,下一个知识点详解)
    4.1 set方法(1 ) 源码和对应的中文注释
    /*** 设置当前线程对应的ThreadLocal的值** @param value 将要保存在当前线程对应的ThreadLocal的值*/public void set(T value) {// 获取当前线程对象Thread t = Thread.currentThread();// 获取此线程对象中维护的ThreadLocalMap对象ThreadLocalMap map = getMap(t);// 判断map是否存在if (map != null)// 存在则调用map.set设置此实体entrymap.set(this, value);else// 1)当前线程Thread 不存在ThreadLocalMap对象// 2)则调用createMap进行ThreadLocalMap对象的初始化// 3)并将 t(当前线程)和value(t对应的值)作为第一个entry存放至ThreadLocalMap中createMap(t, value);} /*** 获取当前线程Thread对应维护的ThreadLocalMap** @paramt the current thread 当前线程* @return the map 对应维护的ThreadLocalMap*/ThreadLocalMap getMap(Thread t) {return t.threadLocals;} /***创建当前线程Thread对应维护的ThreadLocalMap** @param t 当前线程* @param firstValue 存放到map中第一个entry的值*/ void createMap(Thread t, T firstValue) {//这里的this是调用此方法的threadLocalt.threadLocals = new ThreadLocalMap(this, firstValue);}(2 ) 代码执行流程
    A. 首先获取当前线程 , 并根据当前线程获取一个Map
    B. 如果获取的Map不为空 , 则将参数设置到Map中(当前ThreadLocal的引用作为key)