ThreadLocal
介绍
线程本地变量,访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是在操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了并发场景下的线程安全问题
public class ThreadLocalUse { private static final ThreadLocal<DateFormat> DATE_FORMAT_THREAD_LOCAL = new ThreadLocal<DateFormat>(){ @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }; public static Date convert(String source) throws ParseException { return DATE_FORMAT_THREAD_LOCAL.get().parse(source); } /** * @description Removes the current thread's value for this thread-local variable. */ public static void remove(){ DATE_FORMAT_THREAD_LOCAL.remove(); } }
应用场景
日期工具类DateUtil中会用ThreadLocal来让每个线程装在自己的SimpleDateFormat对象
spring提供了事务相关的操作,一次事务的所有操作需要在同一个数据库连接上,但是不需要我们关注
spring就是用 ThreadLocal来实现的,ThreadLocal存储的类型是一个Map
Map中的key是DataSource,value是Connection(为了应对多数据源的情况,所以是一个Map)
用了ThreadLocal保证了同一个线程获取一个Connection对象,从而保证一次事务的所有操作需要在同一个数据库连接上
ThreadLocal原理
threadLocal真正存储数据的是内部类ThreadLocalMap,ThreadLocalMap的引用是在Thread上定义的
Thread类里面有两个变量 ,htreadLocals和inheritableThreadLocals,都是ThreadLocal.ThreadLocalMap类型的变量,当前线程调用ThreadLocal的set、get方法时创建
ThreadLocalMap的key是ThreadLocal,value是set的值,线程内创建多个ThreadLocal共用线程仅有的ThreadLocaLMap
为什么不是Thread作为key?
一个线程可以拥有多个私有变量,key是当前线程的话,还需要用唯一标识来标识set进去的value
并发量大时,所有线程去操作同一个Map,Map体积可能会膨胀,导致访问的性能下降
该Map维护着所有线程的私有变量,意味着不知道什么时候可以销毁
threadLocal作为key,线程销毁了,ThreadLocal也会被销毁
ThreadLocal内存泄露
ThreadLocalMap 中使⽤的 key 为 ThreadLocal 的弱引⽤,⽽ value 是强引⽤。所以,如果ThreadLocal 没有被外部强引⽤的情况下,在垃圾
回收的时候,key 会被清理掉,⽽ value 不会被清理掉。这样⼀来, ThreadLocalMap 中就会出现 key 为 null 的 Entry。假如我们不做任何
措施的话,value 永远⽆法被 GC 回收,这个时候就可能会产⽣内存泄露
ThreadLocalMap实现中已经考虑了这种情况,在调用set()、get(),remove方法的时候,会清理掉key为null的记录。使用完ThreadLocal方法
后,最好手动remove()一下。
概率非常低 ThreadLocal会被两种引用指向
1):ThreadLocalRef->ThreadLocal(强引⽤,被对应的栈引用关联,创建Thread时,栈引用-》Thread-》Thread内部维护了ThreadLocalMap的引用)
2):ThreadLocalMap Entry key ->ThreadLocal(弱引⽤)
只要ThreadLocal没有被回收,那ThreadLocalMap key的指向就不会再GC时 被断开,也就不会有内存泄露
作者: deity-night
出处: https://www.cnblogs.com/deity-night/
关于作者:码农
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接 如有问题, 可邮件(***@163.com)咨询.