5. ThreadLocal使用注意事项使用ThreadLocal结束,一定要调用remove方法,清理掉threadLocal数据 。具体流程类似下面这样:
/** * @author 一灯架构 * @apiNote ThreadLocal示例 **/public class ThreadLocalDemo {// 1. 创建ThreadLocalstatic ThreadLocal<User> threadLocal = new ThreadLocal<>();public void method() {try {User user = getUser();// 2. 给threadLocal赋值threadLocal.set(user);// 3. 执行其他业务逻辑doSomething();} finally {// 4. 清理threadLocal数据threadLocal.remove();}}}
如果忘了调用remove方法,可能会导致两个严重的问题:
- 导致内存溢出
如果线程的生命周期很长,一直往ThreadLocal中放数据,却没有删除,最终产生OOM
- 导致数据错乱
如果使用了线程池,一个线程执行完任务后并不会被销毁,会继续执行下一个任务,导致下个任务访问到了上个任务的数据 。
6.1 ThreadLocal是怎么保证数据安全性的?ThreadLocal底层使用的ThreadLocalMap存储数据 , 而ThreadLocalMap是线程Thread的私有变量,不同线程之间数据隔离,所以即使ThreadLocal的set、get、remove方法没有加锁,也能保证线程安全 。

文章插图
6.2 ThreadLocal底层为什么使用数组?而不是一个对象?因为在一个线程中可以创建多个ThreadLocal实例对象,所以要用数组存储,而不是用一个对象 。
6.3 ThreadLocal是怎么解决哈希冲突的?ThreadLocal使用的线性探测法法解决哈希冲突,线性探测法法具体赋值流程如下:
- 通过key的hashcode找到数组下标
- 如果数组下标位置是空或者等于当前ThreadLocal对象,直接覆盖值结束
- 如果不是空,就继续向下遍历,遍历到数组结尾后 , 再从头开始遍历,直到找到数组为空的位置,在此位置赋值结束
我的猜想是可能是创作者偷懒、嫌麻烦 , 或者是ThreadLocal使用量较少,出现哈希冲突概率较低,不想那么麻烦 。
使用链地址法需要引入链表和红黑树两种数据结构 , 实现更复杂 。而线性探测法没有引入任何额外的数据结构,直接不断遍历数组 。
结果就是,如果一个线程中使用很多个ThreadLocal,发生哈希冲突后,ThreadLocal的get、set性能急剧下降 。
线性探测法相比链地址法优缺点都很明显:
优点: 实现简单,无需引入额外的数据结构 。
缺点: 发生哈希冲突后,ThreadLocal的get、set性能急剧下降 。
6.5 ThreadLocalMap的key为什么要设计成弱引用?先说一下弱引用的特点:
弱引用的对象拥有更短暂的生命周期 , 在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否 , 都会回收它的内存 。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象 。ThreadLocalMap的key设计成弱引用后 , 会不会我们正在使用,就被GC回收了?
这个是不会的,因为我们一直在强引用着ThreadLocal实例对象 。
/** * @author 一灯架构 * @apiNote ThreadLocal示例 **/public class ThreadLocalDemo {// 1. 创建ThreadLocalstatic ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 2. 给ThreadLocal赋值threadLocal.set("关注公众号:一灯架构");// 3. 从ThreadLocal中取值String result = threadLocal.get();// 手动触发GCSystem.gc();System.out.println(result); // 输出 关注公众号:一灯架构}}
由上面代码中得知 , 如果我们一直在使用threadLocal,触发GC后,并不会threadLocal实例对象 。ThreadLocalMap的key设计成弱引用的目的就是:
防止我们在使用完ThreadLocal后,忘了调用remove方法删除数据,导致数组中ThreadLocal数据一直不被回收 。
/** * @author 一灯架构 * @apiNote ThreadLocal示例 **/public class ThreadLocalDemo {// 1. 创建ThreadLocalstatic ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 2. 给ThreadLocal赋值threadLocal.set("关注公众号:一灯架构");// 3. 使用完threadLocal,设置成null,模仿生命周期结束threadLocal = null;// 触发GC,这时候ThreadLocalMap的key就会被回收 , 但是value还没有被回收 。// 只有等到下次执行get、set方法遍历数组,遍历到这个位置 , 才会删除这个无效的valueSystem.gc();}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 深入剖析Sgementation fault原理
- Java程序员必会Synchronized底层原理剖析
- 95年属猪运气在2023年是怎么样的 人生三大方面剖析
- 英雄联盟鳄鱼怎么玩才强 深度剖析鳄鱼玩法
- 行车安全剖析 安全行车细节问题盘点
- 剖析七条富贵竹风水摆放禁忌 盘点富贵竹风水摆放禁忌
- 淮南机动车检测机构 淮南车辆上线检验剖析
- 昌吉车辆上线检验剖析 昌吉汽车检测
- 泰拉瑞亚软核中核硬核什么意思 泰拉瑞亚软核中核硬核区别
- 中山车辆检测 中山车辆上线检验剖析是什么