ThreadLocal原理关于内存泄漏和线程安全的手段

@[TOC](ThreadLocal原理关于内存泄漏和线程安全的手段
#ThreadLocal作为java的一种保证线程安全的手段,和常用的 synchronized等同步手段是有有比较大的区别的。synchronized主要是解决多线程对共享变量的安全操作,ThreadLocal主要是通过保证每个线程有一个自己线程的变量副本进行安全操作

part1:ThreadLocal基本解析

ThreadLocal主要的对外方法

在这里插入图片描述

在这里插入图片描述在这里插入图片描述
从源码我们了解到,三个方式都有用到ThreadLocalMap,他就是ThreadLocal管理线程变量的一个重要方式:
在这里插入图片描述
通过jdk源码总结一下:
1.实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;
2在调用get前要先调用set,我们看到get方法,如果从map中获取不到,就会进行init初始化方法,但是初始化方法返回的null;如果想在get前调用set,就需要重写init方法
3.ThreadLocalMap的key是ThreadLocal,并且map指向一个Entry数组,因为 我们在开发中,一个线程是可能存在多个ThreadLocal变量的,所以需要保存多个变量的副本
基本原理如
在这里插入图片描述

part2:ThreadLocal内存泄漏分析

我们都知道jvm中Java对象的引用 方式,主要是四种
强引用:对象和引用断开连接,gc是的时候才会回收对象数据,是最普遍的引用方式,new对象的时候就是这种方式
软引用:主要是如果内存空间不不足的时候,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存
弱引用:生命周期更短,不管内存是否充足,只要gc都会回收
虚引用:和没有引用一样,任何时候都有可能被回收, 虚引用主要用来跟踪对象被垃圾回收器回收的活动。
通过part1的源码我们可以看到,
ThreadLocalMap内部的Entry是继承的是WeakReference,所以就知道Entry是一个弱引用了,这也是ThreadLocal导致内存泄漏的一个主要原因,ThreadLocal引用关系如下图
在这里插入图片描述如果手动把ThreadLocalRef设置成为null候,然后如果发生gc,ThreadLocal会被回收,key是弱引用指向ThreadLocal,所以key将会被回收,但是value是存在一个Entry强引用(当前线程通过ThreadLocalMap 有一条路径引用Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value,但是现在ThreadLocal如果被回收了,ThreaLocalMap 的key又是ThreadLocal,所以这个时候就无法从ThreaLocalMap 获取到Entry ,所以 value 永远不会被访问到了,所以存在着内存泄露),所以是无法被回收,这个时候value将会内存泄漏
【提问】
1为什么key不强引用ThreadLocal?
key 使用强引用:对 ThreadLocal 对象实例的引用被置为 null 了,但是
ThreadLocalMap 还持有这个 ThreadLocal 对象实例的强引用,如果没有手动删除,
ThreadLocal 的对象实例不会被回收,导致 Entry 内存泄漏。
key 使用弱引用:对 ThreadLocal 对象实例的引用被被置为 null 了,由于
ThreadLocalMap 持有 ThreadLocal 的弱引用,即使没有手动删除,ThreadLocal 的
对象实例也会被回收。value 在下一次 ThreadLocalMap 调用 set,get,remove 都
有机会被回收。
2.为什么不把value设置为弱引用?
因为value只有Entry指向他,如果这是一个弱引用,有可能ThreadLocal还没有回收,value先被回收了,这个时候通过get方法获取的时候,肯定就获取不到了。
总结:
ThreadLocal 内存泄漏的根源是:由于 ThreadLocalMap 的生命周期跟
Thread 一样长,如果没有手动删除对应 key 就会导致内存泄漏,而不是因为弱引

part3 ThreadLocal线程不安全

主要也是引用使用不当导致,ThreadLocal是保证线程私有变量的安全,如果多个线程的变量指向的是同一个堆对象,那其中一个线程改了对象信息,肯定对其他线程是有影响的,所以每个线程的变量引用要保证指向自己的对象
错误例:
在这里插入图片描述
上图中,每个线程的Number的引用 number是独立的,但是所有线程的引用指向的是同一个Number对象–》
解决方案:
1.去掉static
2.初始化的时候,每个引用单独初始一个新的变量
private static ThreadLocal intLocal
= new ThreadLocal(){
@Override
protected Integer initialValue() {
return 1;
}
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值