@@ -1342,3 +1342,57 @@ public ScheduledThreadPoolExecutor(int corePoolSize,
13421342参考链接:
13431343
13441344https: // mp.weixin.qq.com/s?src=11×tamp=1595941110&ver=2487&signature=i8CGBfTlDi4SaG5SSOWYJo-Sgb*bauWAv8MEMYqWQMy4lFBQwjTY4*99R2-8PhC4WtBc4uBy-m3IveQ9a0RlQn53unVD6Xalfl2r30*IbwAdK7CPlbW6-8icKhG4OjKE&new=1
1345+
1346+ ### ThreadLocal 是什么?怎么避免内存泄露?
1347+ 从字面意思上,ThreadLocal 会被理解为线程本地存储,就是对于代码中的一个变量,每个线程拥有这个变量的一个副本,访问和修改它时都是对副本进行操作。
1348+ ##### 使用场景:
1349+ ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景。例如方法直接调用时传递的变量过多,为了代码简洁性,可以使用ThreadLocal ,在前一个方法中,将变量进行存储,后一个方法中取,进行使用。
1350+ ```java
1351+ public class ThreadLocalUtil {
1352+ // 每个线程本地副本初始化
1353+ private static ThreadLocal < UserData > userLocal = new ThreadLocal <>(). withInitial (() - > new UserData ());
1354+ public static void setUser (UserLogin user ){
1355+ if (user == null )
1356+ return ;
1357+ UserData userData = userLocal. get ();
1358+ userData. setUserLogin (user);
1359+ }
1360+ public static UserLogin getUser (){
1361+ return userLocal. get (). getUserLogin ();
1362+ }
1363+ }
1364+ ```
1365+
1366+ ##### 实现原理
1367+
1368+ 就是每个Thread 有一个ThreadLocalMap ,类似于HashMap ,当调用ThreadLocal #set()进行存值时,实际上是先获取到当前的线程,然后获取线程的map,是一个ThreadLocalMap 类型,然后会在这个map中添加一个新的键值对,key就是我们ThreadLocal 实例,value就是我们存的值。ThreadLocalMap 与HashMap 不同的时,解决HashMap 使用的是** 开放定址法** ,也就是当发现hashCode计算得到数组下标已经存储了元素后,会继续往后找,直到找到一个空的数组下标,存储键值对。
1369+
1370+ ```java
1371+ public void set(T value) {
1372+ Thread t = Thread . currentThread();
1373+ ThreadLocalMap map = getMap(t);
1374+ if (map != null )
1375+ map. set(this , value);
1376+ else
1377+ createMap(t, value);
1378+ }
1379+ ThreadLocalMap getMap(Thread t) {
1380+ return t. threadLocals;
1381+ }
1382+ ```
1383+
1384+ ##### ThreadLocal 中的Entry 的key使用了弱引用,为什么使用弱引用?
1385+
1386+ 1. 如果使用强引用,那么Thread 引用了ThreadLocalMap ,ThreadLocalMap 引用了每个key和value,key和value都无法回收,又因为key是ThreadLocal 变量,从而引用了ThreadLocal 的实例对象无法被回收,造成内存泄露。
1387+
1388+ 2. 如果使用弱引用,不会影响key的回收,也就是不会影响引用了ThreadLocal 的实例对象的回收,但是value依然不会被回收,会造成内存泄露。
1389+
1390+ value回收的时机有两个:
1391+
1392+ 1. 我们在用完ThreadLocal 后,手动调用ThreadLocal #remove()对键值对value释放。
1393+
1394+ 2. 此线程在其他对象中使用ThreadLocal 对线程ThreadLocalMap 进行set()和get()时,由于需要进行开放定址法进行探测,会对沿途过期的键值对(就是key为null 的键值对)进行清除。以及set()方法触发的cleanSomeSlots()方法对过期键值对进行清除。
1395+
1396+ ! [thread](.. / static / thread. png)
1397+
1398+ [《一篇文章,从源码深入详解ThreadLocal 内存泄漏问题》](https: // www.jianshu.com/p/dde92ec37bd1)
0 commit comments