ThreadLocal源码解读

本文深入探讨了ThreadLocal的工作原理及其可能导致的内存泄漏问题。解释了ThreadLocal如何使用内部类ThreadLocalMap存储数据,以及为何需要手动调用remove方法以避免内存泄漏。同时介绍了不同类型的引用以及它们在Java中的作用。

经过查询网上资料和看源码解读总结:
1. ThreadLocal 使用内部类ThreadLocalMap存储数据,而ThreadLocalMap的引用(使用或定义)却在Thread类内,所以ThreadLocal 是单独线程使用,不能解决共享对象的多线程访问的竞争问题。
这里写图片描述
2. ThreadLocalMap内部的Entry(存储数据)使用弱引用, 引用ThreadLocal 作为Entry的key值,而当ThreadLocal没有强引用时会被gc回收,ThreadLocalMap得key就会出现null值,这些key为null的value就不能被访问到,而当前线程Thread一直不结束的话,Entry的值value 就会一直有个强引用(Object),gc就永远无法回收,造成内存泄漏。
而为了避免上述情况,ThreadLocalMap在getEntry(ThreadLocal key)方法和set(ThreadLocal key, Object value)内进行了解决:getEntry首先会通过直接索引位置(通过ThreadLocal.threadLocalHashCode & (table.length-1)运算得到 )获取Entry e,如果e不为null并且key相同则返回e,否则会向下一个位置查询,如果查询过程中,key值为空,则设置key对应value为null,这样就不会有强引用,会被gc回收,最终会返回一个e不为空,并且key相等的Entry。
 但是光这样还是不够的,上面的设计思路依赖一个前提条件:要调用ThreadLocalMap的getEntry函数或者set函数。这当然是不可能任何情况都成立的,所以很多情况下需要使用者手动调用ThreadLocal的remove函数,手动删除不再需要的ThreadLocal,防止内存泄露。所以JDK建议将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露。

注:

ThreadLocal.threadLocalHashCode & (table.length-1) 为 int & int 运算 (二进制运算)
按位与运算符“&”,如果两个运算数都是1,则结果为1。其他情况下,结果均为零。
private final int threadLocalHashCode = nextHashCode();
private Entry[] table;

强引用(Strong Reference):通常我们通过new来创建一个新对象时返回的引用就是一个强引用,若一个对象通过一系列强引用可到达,它就是强可达的(strongly reachable),那么它就不被回收
软引用(Soft Reference):软引用和弱引用的区别在于,若一个对象是弱引用可达,无论当前内存是否充足它都会被回收,而软引用可达的对象在内存不充足时才会被回收,因此软引用要比弱引用“强”一些
虚引用(Phantom Reference):虚引用是Java中最弱的引用,那么它弱到什么程度呢?它是如此脆弱以至于我们通过虚引用甚至无法获取到被引用的对象,虚引用存在的唯一作用就是当它指向的对象被回收后,虚引用本身会被加入到引用队列中,用作记录它指向的对象已被销毁。

资料:https://www.cnblogs.com/xzwblog/p/7227509.html
http://blog.youkuaiyun.com/u014326264/article/details/50235963
https://www.cnblogs.com/absfree/p/5555687.html#undefined

ThreadLocal是Java中的一个线程本地变量,它提供了一种线程安全的方式来存储每个线程的局部变量。ThreadLocal的实现原理是在每个线程中创建一个独立的副本,每个线程都可以访问自己的副本,从而避免了线程安全问题。 ThreadLocal的内部结构主要包括ThreadLocal类和ThreadLocalMap类。ThreadLocal类是一个泛型类,用于存储线程本地变量的值。ThreadLocalMap类是一个自定义的哈希表,用于存储每个线程的ThreadLocal变量副本。 在jdk8之前,ThreadLocalMap是通过自定义的Entry数组来实现的,每个Entry包含一个ThreadLocal对象和对应的值。在jdk8之后,ThreadLocalMap的实现方式发生了变化,它使用了类似于HashMap的Node数组来存储Entry,从而提高了性能。 ThreadLocal内存泄漏问题是指在使用ThreadLocal时,由于没有及时清理ThreadLocal变量,导致线程结束后ThreadLocal变量没有被回收,从而导致内存泄漏。解决ThreadLocal内存泄漏问题的方法是在使用完ThreadLocal变量后,调用remove()方法将其从ThreadLocalMap中删除。 下面是一个简单的示例,演示了如何使用ThreadLocal来存储线程本地变量: ```java public class ThreadLocalDemo { private static final ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { Thread t1 = new Thread(() -> { threadLocal.set("Hello from thread 1"); System.out.println(threadLocal.get()); }); Thread t2 = new Thread(() -> { threadLocal.set("Hello from thread 2"); System.out.println(threadLocal.get()); }); t1.start(); t2.start(); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值