java ThreadLocal详解

什么是ThreadLocal?

ThreadLocal 是一个将在多线程中为每一个线程创建单独的变量副本的类;如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本, 在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题。

ThreadLocal的两个作用

  1. 让某个需要用到的对象在线程间隔离(每个线程都有自己的独立的对象)
  2. 在任何方法中都可以轻松获取到该对象

使用场景

  • 每个线程需要一个独享的对象(通常是工具类、典型需要使用的类有SimpleDateFormat和Random)
  • 每个线程内需要保存全局变量(例如在拦截器中获取用户信息),可以让不同方法直接使用,避免参数传递的麻烦

ThreadLocal常用方法

  • initialValue() : 初始化
  • void set(T t) : 为这个线程设置一个新值
  • T get() : 得到这个线程对应的value。如果是首次调用get(),则会调用initialize来得到这个值
  • void remove() : 删除对应这个线程的值

这里着重介绍下get方法:

  • get方法是先取出当前线程的ThreadLocalMap,然后调用map.getEntry方法,把本ThreadLocal的引用作为参数传入,取出map中属于本ThreadLocal的value
  • 注意,这个map以及map中的key和value都是保存在线程中的,而不是保存在ThreadLocal中

ThreadLocalMap是什么呢?

在这里插入图片描述

如上图所示,每一个Thread都有一个TreadLocalMap,而ThreadLocalMap数据结构为key,value格式,key对应的就是ThreadLocal,value对应的是实际需要的成员变量

为了方便大家理解,我们再看下源码:

在这里插入图片描述

当一个ThreadLocal获取当前值时,会先获取到ThreadLocalMap,然后以自身(ThreadLocal)作为key,去获取map中对应的值,让我们再看下ThreadLocal.Entry的数据结构是什么样子的:
在这里插入图片描述
这下是不是很直观了,源码写的很清楚,ThreadLocal作为key去存储值。这里我们可以注意到一个细节,Entry继承的WeakReference,说明ThreadLocalMap的key为弱引用,那么为什么这样设计呢?这就涉及到了内存泄漏的问题。。。

关于ThreadLoca内存泄漏的问题:

接着上个问题说,为什么ThreadLocalMap的key要设计成弱引用而不用强引用?
1.当ThreadLocalMap的key为强引用回收ThreadLocal时,因为ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,就会导致Entry内存泄漏。那么这样显然是不行的。
2.当ThreadLocalMap的key为弱引用回收ThreadLocal时,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。
因此key为弱引用。

key的问题解决了,可是value还是强引用啊,那value会不会出现内存泄漏的问题呢?答案是肯定的!

ThreadLocalMap的每个Entry都是一个对key的弱引用,同事,每个Entry都包含了一个对value的强引用,正常情况下,当线程终止,保存在ThreadLocal里的value会被垃圾回收,因为没有任何强引用了,但是如果线程不终止,那么key对应的value就不能被回收,因为会产生一下调用链:
Thread -> ThreadLocalMap -> Entry(key为null) -> value
那么如何解决这个问题呢?让我们再看一个源码:
在这里插入图片描述
这下懂了吧!源码写的很清楚了(Help the GC!!!)JDK已经帮我们考虑到这个问题了,在ThreadLocal里的set、remove、resize等方法里,都会扫描key为null的Entry,并把对应的value设置为null,这样value对象就可以被回收了,内存泄漏的问题就解决了。那又该有人说了:如果一个ThreadLocal不被使用了,那么set、remove、resize方法就不会被调用了,如果线程也不停止,不就又导致内存泄漏了吗?
为了避免上述情况的出现,阿里规约给出了答案。

使用完ThreadLocal之后,应该调用remove方法!!!
使用完ThreadLocal之后,应该调用remove方法!!!
使用完ThreadLocal之后,应该调用remove方法!!!

这样就可以避免内存泄露了。。。┗( ▔, ▔ )┛

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值