1:什么是ThreadLocal类,怎么使用它
ThreadLocal为每个线程维护一个本地变量。
采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。
ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本。
ThreadLocal在Spring中发挥着巨大的作用,在管理Request作用域中的Bean、事务管理、任务调度、AOP等模块都出现了它的身影。
Spring中绝大部分Bean都可以声明成Singleton作用域,采用ThreadLocal进行封装,因此有状态的Bean就能够以singleton的方式在多线程中正常工作了。
2:底层原理
Thread、ThreadLocal、ThreadLocalMap关系
在每个Thread对象中都持有一个ThreadLocalMap成员变量。
一个Thread对象中可能拥有多个ThreadLocal对象
ThreadLocalMap类也就是Thread.threadLocals
ThreadLocalMap类是每个线程Thread类里面的变量,里面最重要的是一个键值对数组Entry[] table,可以认为是一个map,键值对:
键:这个ThreadLocal
值:实际需要的成员变量,如user或simpleDateFormat对象
首先 ThreadLocal 是一个泛型类,保证可以接受任何类型的对象。
因为一个线程内可以存在多个 ThreadLocal 对象,所以其实是 ThreadLocal 内部维护了一个 Map ,这个 Map 不是直接使用的 HashMap ,而是 ThreadLocal 实现的一个叫做 ThreadLocalMap 的静态内部类。而我们使用的 get()、set() 方法其实都是调用了这个ThreadLocalMap类对应的 get()、set() 方法。
3:内存泄露
1:什么是内存泄露
某个对象不再有用,但是占用的内存不能回收,导致OOM
2:内存泄露的原因
WeakReference是弱引用,将key用弱引用进行赋值。
弱引用的特点是,如果这个对象只被弱引用关联,没有任务强引用关联。这个对象就可以被GC回收。而value是强引用,GC不能回收。就会导致key被回收,而value还在。
正常情况下,当线程终止,线程会回收,保存在ThreadLocal里的key和value会被垃圾回收。
但是如果线程不终止,那么key对应的value就不能被回收。(比如在线程池中创建的线程,这些线程会反复利用,很难终止)因此会有以下调用链
3:解决
在set、remove、rehash方法中会扫描key为null的Entry,并把对应的value设置为null,这样value对象就可以被回收。
但是如果一个ThreadLocal不被使用,则实际上set、remove、rehash方法也不会被调用,同时线程又不停止,则调用链一直存在,就会导致内存泄露。
阿里规约:在调用remove方法,就会删除对应的Entry对象,可以避免内存泄露,所以使用完ThreadLocal之后,就调用remove方法