ThreadLocal对象可以提供线程局部变量,每个线程Thread拥有一份自己的副本变量,多个线程互不干扰。
ThreadLocal
的数据结构
- 每一个 Thread 对象均含有一个 ThreadLocalMap 类型的成员变量 threadLocals ,它存储本线程中所有ThreadLocal对象及其对应的值
- ThreadLocalMap有自己的独立实现,可以简单地将它的key视作ThreadLocal,value为代码中放入的值(实际上key并不是ThreadLocal本身,而是它的一个弱引用)。ThreadLocalMap有点类似HashMap的结构,只是HashMap是由数组+链表实现的,而ThreadLocalMap中并没有链表结构。
- 每个线程在往ThreadLocal里放值的时候,都会往自己的ThreadLocalMap里存,读也是以ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程隔离。
- 我们还要注意Entry, 它的key是ThreadLocal<?> k ,继承自WeakReference, 也就是我们常说的弱引用类型。一个 Entry 由 ThreadLocal 对象(key)和 Object 构成。
Java四种引用类型
- 强引用:我们常常new出来的对象就是强引用类型(或者反射创建的对象),只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足的时候
- 软引用:使用SoftReference修饰的对象被称为软引用,软引用指向的对象在内存要溢出的时候被回收
- 弱引用:使用WeakReference修饰的对象被称为弱引用,只要发生垃圾回收,若这个对象只被弱引用指向,那么就会被回收
- 虚引用:虚引用是最弱的引用,在 Java 中使用 PhantomReference 进行定义。虚引用中唯一的作用就是用队列接收对象即将死亡的通知
ThreadLocal的set方法分析
- set方法实际是set到当前线程(先创建)的map中,key就是ThreadLocal对象,value要存储值
- 将key指向ThreadLocal的引用改为弱引用(如果是强引用ThreadLocal对象不能被回收,内存泄露),可以回收,但是key( == null)访问不到value了(内存泄露),一定要调用remove()掉!
ps:为什么不等到当前线程自己销毁?(1)可能长时间不关闭(2)可能是线程池的线程,用完归还
ThreadLocal应用场景
- 在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。可以看成属于线程的一个全局变量。设置在线程本地,如果设置成static多线程存在安全问题
- Spring框架在事务开始时会给当前线程绑定一个Jdbc Connection,在整个事务过程都是使用该线程绑定的connection来执行数据库操作,实现了事务的隔离性。Spring框架里面就是用的ThreadLocal来实现这种隔离。
- 即@Transational,首先保证在拿到的是同一个连接,才能保证在同一个事务中,解决方案:把connection放到ThreadLocal,任何被事务所管理的方法,用到connection去ThreadLocal取,而不是去连接池取,永远都是同一个connection。