zoukankan      html  css  js  c++  java
  • 对ThreadLocal的理解个人

    ThreadLocal提供一个方便的方式,可以根据不同的线程存放一些不同的特征属性,可以方便的在线程中进行存取;

    简单一句话,自己的线程里的东西自己玩,别人不能动。达到隔离的效果,就不存在线程安全的问题了;

    举例子:1000个线程任务打印时间:

    结果:用dateFormat的时候,会出现打印出相同的时间的结果,原因是所有线程共用一个SimpleDateFormat对象的时候,它发生了线程安全的问题,而用dateFormatThreadLocal解决了;

    原理;先沾出来ThreadLocal的源码

     简化下就如下:

    Thread.currentThread().threadLocals.set(this,value);
    Thread.currentThread()获取当前的线程
    threadLocals就是每个线程对象中用于存放局部对象的map

    threadLocals可以简单粗暴的理解为,每一个线程都自带一个map,map的key为dateFormatThreadLocal,value为SimpleDateFormat对象;这样这个对象就隔离了,自己用自己的;

    ThreadLocal就是一个标记的作用,当我们在线程中使用ThreadLocal的set()或者get()方法时,其实是在操作我们线程自带的threadLocals这个map,多个线程的时候自然就有多个map,这些map互相独立,但是,这些map都是根据一个ThreadLocal对象(因为它是静态的)来作为键存放。

      这样可以在多个线程中,每个线程存放不一样的变量,我们通过一个ThreadLocal对象,在不同的线程(通过Thread.currentThread()获取当前线程)中得到不同的值(不同线程的threadLocals不一样)。

      为什么threadLocals要是一个map呢?

      因为我们可能会在一个类中声明多个ThreadLocal的实例,这样就有多个标记,所以要使用map对应。

     ThreadLocal这么吊了,用它时候要注意了,他可能产生内存泄漏!!

    threadLocal,threadLocalMap,entry之间的关系如下图所示:

     
    上图中,实线代表强引用,虚线代表的是弱引用,如果threadLocal外部强引用被置为null(threadLocalInstance=null)的话,threadLocal实例就没有一条引用链路可达,很显然在gc(垃圾回收)的时候势必会被回收,因此entry就存在key为null的情况,无法通过一个Key为null去访问到该entry的value。同时,就存在了这样一条引用链:threadRef->currentThread->threadLocalMap->entry->valueRef->valueMemory,导致在垃圾回收的时候进行可达性分析的时候,value可达从而不会被回收掉,但是该value永远不能被访问到,这样就存在了内存泄漏。当然,如果线程执行结束后,threadLocal,threadRef会断掉,因此threadLocal,threadLocalMap,entry都会被回收掉。可是,在实际使用中我们都是会用线程池去维护我们的线程,比如在Executors.newFixedThreadPool()时创建线程的时候,为了复用线程是不会结束的,所以threadLocal内存泄漏就值得我们关注。

    为什么使用弱引用?

    通过threadLocal,threadLocalMap,entry的引用关系看起来threadLocal存在内存泄漏的问题似乎是因为threadLocal是被弱引用修饰的。那为什么要使用弱引用呢?

    如果使用强引用

    假设threadLocal使用的是强引用,在业务代码中执行threadLocalInstance==null操作,以清理掉threadLocal实例的目的,但是因为threadLocalMap的Entry强引用threadLocal,因此在gc的时候进行可达性分析,threadLocal依然可达,对threadLocal并不会进行垃圾回收,这样就无法真正达到业务逻辑的目的,出现逻辑错误

    如果使用弱引用

    假设Entry弱引用threadLocal,尽管会出现内存泄漏的问题,但是在threadLocal的生命周期里(set,getEntry,remove)里,都会针对key为null的脏entry进行处理。

    从以上的分析可以看出,使用弱引用的话在threadLocal生命周期里会尽可能的保证不出现内存泄漏的问题,达到安全的状态。




    总结下用threadlocal的最佳实践:

    • 每次使用完ThreadLocal,都调用它的remove()方法,清除数据。
    • 在使用线程池的情况下,没有及时清理ThreadLocal,不仅是内存泄漏的问题,更严重的是可能导致业务逻辑出现问题。所以,使用ThreadLocal就跟加锁完要解锁一样,用完就清理。

    引用:https://www.jianshu.com/p/dde92ec37bd1
    当你发现自己的才华撑不起野心时,就请安静下来学习吧!
  • 相关阅读:
    手脱ASPack2.12壳(练习)
    手脱UPX3.91壳(练习)
    如何在程序执行前插入可执行代码(算是吾爱的作业吧,活学活用)
    小米笔记本粉丝感恩狂欢趴【慎入!!】
    picpick截屏软件脱壳
    JAVA反序列化漏洞
    Java中的数据类型
    Java中常见的包
    Scrapy
    PHPstorm 函数时间注释的修改
  • 原文地址:https://www.cnblogs.com/longchuqianyuan/p/12682467.html
Copyright © 2011-2022 走看看