首页 > 编程知识 正文

threadlocal原理和缺点,threadlocal原理和作用

时间:2023-05-05 10:42:53 阅读:281366 作者:33

底层原理

实际上是每个Thread线程维护着一个ThreadLocalMap,ThreadLocalMap底层是一个带有key,value的Entry数组,而Entry的键即是一个ThreadLocal对象,value是我们通过ThreadLocal.set()方法传进去的参数
ThreadLocal.java:
一个ThreadLocal对象只能存储一个value


Thread.java:

threadlocal.ThreadlocalMap.java:

threadlocal.ThreadlocalMap.Entry.java:

整体结构:
注意,一个线程里如果设置了多个ThreadLocal对象,那么他们在同一个ThreadLocalMap里
也就是说 ThreadLocal 本身并不存储值,它只是作为一个 key 来让线程从 ThreadLocalMap 获取 value

内存泄漏 为什么ThreadLocal是弱引用

准确来说,是ThreadLocal.ThreadLocalMap.Entry是弱引用。注意,不是ThreadLocal本身是弱引用

ThreadLocal在使用的时候,存在两条引用链:
1.业务代码中创建的ThreadLocal对象的强引用
2.线程Thread中的ThreadlocalMap的Entry是弱引用
使用弱引用,这样在业务代码置ThreadLocal为null或者ThreadLocal在使用结束后,因为是弱引用Entry引着ThreadLocal,那么ThreadLocal可以被回收,但是Entry还在的

内存泄漏的原因

那么也就是说,ThreadLocal对象我们程序用完了由于从 程序->ThreadLocal 这条强引用链不存在了,而线程本身->ThreadLocalMap->Entry->ThreadLocal 这条链又是一条虚引用链,那么GC的时候ThreadLocal就会被回收了。但是使用ThreadLocal对象作为key的这些Entry本身是不会被清除的,为什么呢?因为存在一条强引用链。即线程本身->ThreadLocalMap->Entry,而有由于作为Entry的key对象的ThreadLocal被回收了,这样就会在ThreadLocalMap中存在一些key为null的Entry。因为key变成null了,我们是没法访问这些Entry的,但由于Entry中还保存着value,value可能还占用着一大块内存,那么这部分内存就相当于内存泄漏了,因为他占用着内存没法回收,而我们又没法访问到它。
理论上,这条线程结束,这部分内存就会被回收,但是我们在使用线程池的时候,线程使用完了是会放回到线程池循环使用的。由于ThreadLocalMap的生命周期和线程一样长,如果没有手动删除对应key就会导致这块内存即不会回收也无法访问,也就是内存泄漏

ThreadLocal的补救

其实,ThreadLocalMap的设计中已经考虑到这种情况,也加上了一些防护措施:在ThreadLocal的get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value。但是这些举动不能保证内存就一定会回收,因为可能这条线程被放回到线程池里后再也没有使用,或者使用的时候没有调用其get(),set(),remove()方法
内存泄漏归根结底是由于ThreadLocalMap的生命周期跟Thread一样长

如何避免内存泄漏

每次使用完ThreadLocal,都调用它的remove()方法,清除数据

应用

建一个threadloca管理类,静态方法存取值即可。
可能有人有疑问:ThreadLocal对象是类的静态属性,那多个线程都用同一个属性不是有冲突吗?
别忘了,ThreadLocal这个属性是一个key而已,但是map在不同的线程,他们存取在不同的Thread,不会相互影响

public class UserContext implements Serializable { private static ThreadLocal<AccountDto> accountDtoThreadLocal = new ThreadLocal<>(); public static AccountDto getAccountDto() { Thread a = Thread.currentThread(); System.out.println(a.getName()); return accountDtoThreadLocal.get(); } public static void setAccountDto(AccountDto accountDto) { Thread a = Thread.currentThread(); System.out.println(a.getName()); accountDtoThreadLocal.set(accountDto); } public static void removeUserSession() { accountDtoThreadLocal.remove(); }}

参考:https://blog.csdn.net/qq32933432/article/details/80212873

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。