线程安全的天选之子ThreadLocal

源于蚂蚁课堂的学习,点击这里查看(老余很给力)    

一、前言

       物欲横流的信息时代下,性能和效率是产品在百花齐放中脱颖而出的致胜法宝。作为软件研发人员,在物理硬件固定的情形中,如何确保程序高效吞吐赫然是必备的看家本领。而提升效率就会涉及老生常谈的多线程处理,那么对于这种高效手段需要注意的线程安全问题,相比很多猿友一定耳濡目染了,今天帝都的雁为大家带来一种线程安全的技术手--ThreadLocal。(PS:需要有JVM垃圾回收、强引用、软引用、弱引用以及虚引用等相关理论基础)

二、用法

       ThreadLocal<T> threadLocal = new ThreadLocal<>();
       在程序中我们直接创建ThreadLocal即可。
       ThreadLocal常用的API如下:

       threadLocal.set(T t);
       T t = threadLocal.get();
       threadLocal.remove();
    
       细心的猿友会发现ThreadLocal的使用有点类似于map,真的是这样吗?

三、源码解读

1、set(T value)

2、getMap(Thread t)

3、ThreadLocalMap

4、get()

​5、remove()

       看完源码后,感觉还是比较简单的,你是否会有疑问:为什么要去使用ThreadLocal呢?或者它有哪些优点值得我们学习使用?

四、线程安全的特性

       由于ThreadLocal在每个线程中都存放了副本数据,所以线程内部处理这些数据时,天然线程隔离,彼此互不干扰,是一种典型的空间换取时间的高效处理方式。

       不同于synchronized,synchronized是采用同步阻塞的方式进行时间换取空间,在高并发下,惨不忍睹的效率局限了其使用范围。

五、使用场景

ThreadLocal在很多主流框架中都进行了应用。
1、Spring的声明式事务
2、Pagehelper分页插件
3、SpringMVC的请求链

        我们自己也可以广泛地去使用它,比如:当我们接口传递参数较多时,多个接口之间不想重复去写这么多参数名称,既冗余又逼格低,我们可以采用ThreadLocal去将这些参数放入此次请求的线程中,使用时从线程中获取出来。  

六、内存泄漏问题

      这是ThreadLocal使用上需要特别注意的地方。

       何为内存泄漏?对象使用结束,且不会被再次使用到,那么这个对象实际上就是一个垃圾,需要被垃圾回收器进行回收,但如果因为某种原因导致本该回收的垃圾对象,无法被回收掉。这时,就出现了内存泄漏。久而久之,堆内存持久增长,最终导致内存溢出。

       为什么ThreadLocal就会出现内存泄漏呢?由源码可看出Entry继承了弱引用。那么,我们就先来看看引用的类型吧。 

1、强引用

直接指向对象的引用,如Object o1 = new Object(); Object o2 = o1;

这时o1和o2都是强引用,即使发生垃圾回收,也不会被回收掉。

2、软引用

  堆内存不足,进行垃圾回收时,软引用指向的对象会被回收掉。

3、弱引用

  WeakReference,只要进行垃圾回收,弱引用指向的对象一定会被回收掉。

4、虚引用

虚引用执行的对象随时都会被回收掉,常用于记录对象存活状态。

        故此,我们可以得出:ThreadLocal在ThreadLocalMap中以弱引用的方式存放数据。当有垃圾回收时,ThreadLocal执行的对象就会被垃圾回收器收集,释放占用内存,使得其原本的<ThreadLocal, Value>变为<null, Value>。这个Entry永远不会被再次使用到了,但却由于其存放在线程的ThreadLocalMap中,导致其无法被垃圾回收器收集,从而发生内存泄漏。

        所以,我们在使用ThreadLocal时,尽量使用之后使用其remove方法手动删除,避免内存泄漏。

七、垃圾回收机制

        JAVA优于其它语言的特性之一在于,无需研发者手动是否对象的内存空间,交由JAVA自身的垃圾回收机制去收回这些不再使用的对象内存。那么,垃圾回收是怎么触发的呢?

        首先,我们需要明确JVM的内存结构。按照线程独占和线程共享可大致分为如下:

1、线程独占

      栈:存放引用地址,由一个个栈帧组成,栈帧中又由本地变量表、操作数栈、动态连接和返回地址组成。

      本地方法栈:存放与C语言进行通讯的接口。

      程序计数器:记录当前线程执行至哪一行代码。

2、线程共享

      堆:存放创建出来的对象。

      元空间(静态方法区):存放class的相关信息、静态信息等

        在对JAVA内存结构有了一定轮廓后,就需要分析垃圾回收机制的原理。我们知道,堆内存是存放创建出来的对象,那么当堆内存满了的话,有程序再去申请内存空间,就会发生内存溢出的异常OutOfMemory(简称:OOM)。JAVA为了减少这种异常,人性化的设置了垃圾回收机制,当堆内存的对象成为垃圾时,会被垃圾回收器回收掉内存。而垃圾回收器判断对象是否要被回收的依据就是对象是否可达(与堆内存外的数据地址有联系),然后依照自身的机制去做对应算法分析后,进行垃圾处理。

欢迎大家和帝都的雁积极互动,头脑交流会比个人埋头苦学更有效!共勉!

公众号:帝都的雁

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值