1 什么是ThreadLocal?
    多个线程同时访问同一个共享变量会造成并发问题
    而ThreadLocal会为每一个线程提供一个独立的变量副本 从而隔离了多个线程对数据的访问冲突
    因为每一个线程都拥有自己的变量副本 从而也就解决了多线程的并发访问问题
    在很多情况下 ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单更方便 且结果程序拥有更高的并发性
    2 在java.lang包下
    3 ThreadLocal类直接继承Object类
    4 只有一个无参构造方法
    ThreadLocal local = new ThreadLocal();
    5 该类下常用方法为
    4.1 set(T value);
    //往里存值
    4.2 T = get();
    //取值
    4.3 remove();
    //将当前线程存的局部变量移除
    //就算不显示调用这个方法 当线程结束后 这局部变量也会自动被垃圾回收
    //但是最好显示调用 不然可能会造成内存泄漏
    set源码解析:

    1. public void set(T value) {
    2. Thread t = Thread.currentThread();
    3. //获取当前线程对象
    4. ThreadLocalMap map = getMap(t);
    5. //通过当前线程对象 t 获取到线程私有的 ThreadLocalMap 对象(ThreadLocalMap是ThreadLocal的静态内部类)
    6. //也就是说每一个线程都有自己的 ThreadLocalMap
    7. if (map != null)
    8. //如果map不为空 往里存值
    9. map.set(this, value);
    10. else
    11. //如果map为空 就先创建map对象
    12. createMap(t, value);
    13. }
    14. //ThreadLocalMap解决hash冲突的方式采用的是线性探测法 如果发生冲突就继续找下一个空位置

    get源码分析:

    1. public T get() {
    2. Thread t = Thread.currentThread();
    3. //获取当前线程对象
    4. ThreadLocalMap map = getMap(t);
    5. //通过当前线程对象 t 获取到线程私有的 ThreadLocalMap 对象
    6. if (map != null) {
    7. ThreadLocalMap.Entry e = map.getEntry(this);
    8. //通过this(即ThreadLocal对象)找到Entry对象
    9. if (e != null) {
    10. //Entry对象中的值不为空 就将值返回
    11. T result = (T)e.value;
    12. return result;
    13. }
    14. }
    15. return setInitialValue();
    16. //如果找不到就将当前ThreadLocal对象和变量作为键值对存入到ThreadLoclaMap中并返回变量
    17. }

    6 可以类比session来了解ThreadLocal存值
    1 String JSESSIONID(标识) ——————-> Thread t 当前线程(标识 因为线程是唯一存在的)
    2 通过标识找session(可以理解为储物柜) ——————-> 底层通过 t 找 ThreadLocalMap(可以理解为储物柜)
    3 通过session.setAttribute(key,value);存值 ——————-> 底层通过 ThreadLocalMap.set(this,value); 存值
    key自己定义 在key不同的情况下可以存好多值 注意这里key已经固定了 为this 表示调用这个方法的当前对象(ThreadLocal对象)
    因为key是固定的 所以只能存一个信息 后续存入的都会把前面的覆盖
    想要存储多个信息 可以将多个信息包装起来在存
    7 区分 Thread ThreadLocal ThreadLocalMap
    ThreadLocal是Thread的局部变量 一个Thread可以有多个ThreadLocal
    ThreadLocalMap是ThreadLocal的静态内部类 用来管理多个ThreadLocal的
    ThreadLocalMap底层就是Entry数组 Entry中存的是ThreadLocal的引用和当前ThreadLocal存的值value
    8 使用场景
    比如在MVC分层中 service业务层通过调用dao层获取到了数据库中的记录
    但service层只负责做逻辑判断 只会把判断后的结果返回到controller控制层 那么控制层就不能直接获取数据库中的记录
    但我们又想在控制层获取到这些记录 但是若让service层返回一条记录很明显不符合分层架构的要求
    所以这时候就用到了ThreadLocal 可以在service层将记录存到ThreadLocal中 然后在控制层获取
    但是如何保证获取到的是同一个ThreadLocal对象呢?还需要设计一个类 负责管理ThreadLocal
    比如

    1. public class ThreadLocalManager {
    2. //管理不同的ThreadLocal对象 一个人分配一个ThreadLocal
    3. //用每一个人登录的账号作为key 每一个ThreadLocal对象作为值
    4. private static HashMap<String,ThreadLocal> localMap = new HashMap<>();
    5. //通过登录账号获取自己对应的那一个local对象
    6. public static ThreadLocal getThreadLocal(String name){
    7. ThreadLocal local = localMap.get(name);
    8. if(local==null){
    9. local = new ThreadLocal();
    10. localMap.put(name,local);
    11. }
    12. return local;
    13. }
    14. }

    9 ThreadLocal会导致内存泄漏
    XLU__CKX[990Z`W0R9T1M]T.png
    (WeakReference本身是强引用,它内部的才是真正的弱引用,也就是说WeakReference是用来装弱引用的容器而已)

    ThreadLocalMap中的Entry继承自WeakReference,使得Entry的key是弱引用
    所以ThreadLocal 在没有外部强引用时,发生 GC 时会被回收
    那么ThreadLocalMap中就会存在key 为null 的 Entry 对象
    而如果创建当前线程一直运行的话,那么这个Entry 对象中的 value 就可能一直得不到回收,那么就导致了内存泄漏
    (Entry 被 ThreadLocalMap 对象引用,ThreadLocalMap 对象又被 Thread 对象所引用)
    10 如何避免内存泄漏
    在使用完 ThreadLocal 后,手动调用 remove 方法即可
    (其实也不用太过在意内存泄露的问题,因为ThreadLocalMap在内部set或者get的时候都会清理掉key为null的Entry)