一、前言

1. 什么是ThreadLocal

多线程通信的几种方式

在多线程编程下,解决各线程数据之间的同步问题,synchronized需要线程之间频繁的加锁、解锁、唤醒等操作,还要自己手动维护好不同线程之间的数据。
threadLocal为每个线程维护了自己的数据,保证各线程之间数据的隔离性。

2. 什么是内存泄漏

首先我们先看下内存泄漏的百度定义:

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

3. 什么是弱引用

定义:

弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存

重点理解只具有弱引用一个对象同时被弱引用和强引用的时候,gc时不会被回收,如果一个对象只是被弱引用那么gc就会回收这个对象

二、ThreadLocal原理分析

thread、threadLocal、threadLocalMap、entry、value引用链:
image

  1. thread引用threadLocalMap
  2. threadLocalMap引用entery
  3. entry引用两个值,分别是k持有threadLocal的弱引用,v持有threadlocal.set()值的引用
  4. 堆内存中的threadLocal被entry.key弱引用和threadLocalRef强引用

所以如果把threadLocal的对象置为null的时候,那么只存在弱引用的ThreadLocal在gc时就会被回收掉,entry.key的也会变为null,entry.value这个时候还存在强引用,没办法回收掉但是又无法根据key在访问到这个value。如果线程迟迟不能结束,那么就会由内存泄漏导致内存溢出了。
手动把threadLocal置为null:
`

// ThreadLocal<String> 被变量threadLocal强引用  
ThreadLocal<String> threadLocal = new ThreadLocal<>();
// ThreadLocal<String> 被entry.key弱引用
threadLocal.set("test");
// 去除强引用
threadLocal = null;

`

三、如何解决内存泄漏

  1. 使用完threadLocal后,手动调用remove().
  2. 使用线程池的情况下,线程会被复用,要注意threadlocal的数据被下一个业务复用导致业务出错。