ThreadLocal
前置知识
Java中有些引用类型?
Java中主要有4种引用类型,分别是:强、软、弱、虚。他们主要跟Java的垃圾回收机制有关
强引用:Java中默认的引用类型,一个对象如果具有强引用,那么只要这种引用还存在就不会被回收
软引用:在内存充足时,是不会GC这个对象的。只有在JVM内存不足的时候才会调用垃圾回收器回收掉这个对象
弱引用:弱引用所引用的对象只能生存到下次GC之前,当发生GC的时候,无论当前内存是否足够,弱引用所引用的对象都会被回收掉。它是和强引用一起配合使用,用来解决内存泄漏问题,比如我们今天所聊的ThreadLocalMap中的Entry对象,就使用了弱引用
虚引用:虚引用是所有引用中最弱的一种引用,其存在就是为了将关联虚引用的对象在被GC掉之后收到一个通知
基础理解
ThreadLocal是Java所提供的线程本地存储机制,是用来解决Java多线程并发问题的一种途径,通过为每一个线程创建一分共享变量的副本,来保证各个线程之间的变量的访问和修改互不影响
内部原理
Thread类中维护了ThreadLocalMap成员变量
而ThradLocalMap维护了以ThreadLocal为key需要存储数据为value的Entry数组
Entry实际上继承了一个ThreadLocal类型的弱引用,并在其作为key,value为Object类型
内存泄漏问题
ThreadLocal对象是有两个引用的
一个是栈上的ThreadLocal引用
一个是ThreadLocalMap的Entry对象的key对他的引用
假如栈上的ThreadLocal引用已经不再使用了,因为用了弱引用,那么ThreadLocal对象在下次GC时,就会被垃圾回收掉,这样做可以很大程度的避免因为使用ThreadLocal的使用而导致的OOM问题,但是这个问题却无法彻底避免,虽然key是弱引用,但是value却是强引用,而且它的生命周期是和Thread一样的,也就是说只要Thread还在,这个对象就无法被回收,Thread对象长期存在的典型案例就是线程池,在线程池中重复利用线程的时候,就会导致这个引用一直在,导致value一直无法被回收
解决方案
当我们每一个ThreadLocal用完之后,手动调用remove()方法,就可以在下次GC的时候,把Entry清理掉
使用场景
1.使用日期工具类,当用到SimpleDateFormat作为成员变量时,可以使用ThreadLocal保证线程安全
2.全局存储用户信息
3.保证一个线程获取到的数据库连接Connection是同一个
4.Spring框架中也大量使用了ThreadLocal保证线程安全