美文网首页
ThreadLocal

ThreadLocal

作者: hehehehe | 来源:发表于2020-06-11 23:46 被阅读0次

    让某个需要用到的对象在线程间隔离,每个线程有自己独立的对象

    两大场景

    1、每个线程需要一个独享的对象(通常是工具类,比如

    SimpleDateFormat、Random等线程不安全的工具类)

     private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
            @Override
            protected SimpleDateFormat initialValue()
            {
                return new SimpleDateFormat("yyyyMMdd HHmm");
            }
        };
    
    private static final ThreadLocal<SimpleDateFormat> formatter 
    = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd HHmm"));
    

    每个Thread内有自己的副本,不共享。

    2、每个线程需要保存全局产量(例如拦截器拦截的用户信息),可以让不同的方法直接使用,避免参数传递的麻烦

    用TheadLocal保存一些业务内容(用户权限信息,用户名,userId等信息)
    这些信息在同一个线程内相同,不同线程之间不同
    强调的是同一个请求(同一个线程内),不同方法之间的共享
    不需要重写initialValue() 方法,但是必须手动调用set方法

    private static ThreadLocal<User> holder= new ThreadLocal<User>()
    

    每个Thread 对象都持有一个ThreadLocalMap对象、
    一个ThreadLocalMap里有多个ThreadLocal对象

    ThreadLocalMap

    ThreadLocalMap类,就是Thread.threadLocals
    ThreadLocalMap 的一个最重要的是一个数据Entry[] table,可以认为是一个map
    key:这个ThreadLocal
    value:实际需要的变量值

    initalValue()

    1、该方法返回当前线程对象的初始值,这是一个延迟加载的方法,只有在调用get的时候才会触发
    2、如果在get之前使用了set方法,将不会再触发initialValue方法
    3、通常,每个线程最多调用一次initialValue方法,但是如果调用了remove方法后,再调用get方法,则会再次调用此方法
    4、如果不重写该方法,该方法会返回null,需要我们自己重写该方法

    get()

    get方法首先取出当前线程的ThreadLocalMap,然后调用map.getEntry方法,把本ThreadLocal引用作为参数传递,取出map中属于本TheadLocal的value
    这个map以及map中的key value都是保存在线程中的,而不是ThreadLocal中,因为map是thread持有

    remove()

    能够删除当前线程对应的ThreadLocal的值

    内存泄漏问题

    C c = new C(b);
    b = null;
    考虑下GC的情况。要知道b被置为null,那么是否意味着一段时间后GC工作可以回收b所分配的内存空间呢?答案是否定的,因为即便b被置为null,但是c仍然持有对b的引用,而且还是强引用,所以GC不会回收b原先所分配的空间!既不能回收利用,又不能使用,这就造成了内存泄露。
    那么如何处理呢?
    可以c = null;也可以使用弱引用!(WeakReference w = new WeakReference(b);)

    ThreadLocalMap

    首先来说,如果把ThreadLocal置为null,那么意味着Heap中的ThreadLocal实例不在有强引用指向,只有弱引用存在,因此GC是可以回收这部分空间的,也就是key是可以回收的。但是value却存在一条从Current Thread过来的强引用链。因此只有当Current Thread销毁时,value才能得到释放。
    key是弱引用
    value是强引用
    1、正常情况下线程结束,里面所有东西都会被回收
    2、如果线程始终不终止(线程池),value强引用无法被回收,
    jdk已经考虑到这个问题,在set remove,rehash方法中会扫描所有key为null的Entry,并吧对象value置为null,这样value会被回收
    但是实际中,我们忘记调用这些方法
    3、如果一个ThreadLocal不被使用了,那么实际中set remove,rehash方法也不会被调用了,如果线程又不会被终止,那么就会导致value的内存泄漏 (threadLocal设为null和线程结束这段时间内不会被回收的,就发生了我们认为的内存泄露)
    我们使用完之后(比如调用链的最后一端,很清楚不会再使用了)应该手动进行删除调用remove方法清除当前ThreadLocal对象
    个人理解:正常情况下ThreadLocal对象如果一直使用就不会出现内存泄漏,因为ThreadLocalMap的key是当前ThreadLocal对象,当前对象没有被回收,value就还有用,不算内存泄漏
    当一个不再使用的ThreadLocal,那么key可能就被回收了,导致value的泄漏

    不应该在ThreadLocal中放静态共享对象

    相关文章

      网友评论

          本文标题:ThreadLocal

          本文链接:https://www.haomeiwen.com/subject/dcdwtktx.html