关于ThreadLocal不得不说的内容

概念

每个Thread对象里有一个map,key是ThreadLocal的hashcode,value是我们放入的Object。每个线程都独享一块ThreadLocal Map。
在这里插入图片描述

内存泄漏

Map的Entry继承自WeakReference,所以它的key是弱引用。如果是强引用,即使threadlocal=null,key的引用仍然指向对象,造成内存泄漏。

但在某些情况下还是会有内存泄漏的情况

  1. 当使用static ThreadLocal的时候,延长了ThreadLocal的生命周期,也可能导致内存泄漏。因为static变量在线程结束的时候不一定会回收。那么,比起普通成员变量使用的时候才加载,static的生命周期加长将更容易导致内存泄漏危机

  2. 线程没结束,但是ThreadLocal没了被置成null,造成value不可访问也不销毁,这时候可能会内存泄漏。

优化

JVM利用调用remove、get、set方法的时候,都会回收弱引用对象

Java为了最小化减少内存泄露的可能性和影响,在ThreadLocal的get,set的时候都会清除线程Map里所有key为null的value。所以最坏的情况就是,threadLocal对象设null了,开始发生“内存泄露”,然后使用线程池,这个线程结束后放回线程池中不销毁且一直不被使用,或者使用了又不再调用get,set方法,那么这个期间就会发生真正的内存泄露。

源码

在ThreadLocal类中set(v)方法会调用ThreadLocalMap的set(this, v)方法

get()方法内调用 ThreadLocalMapgetEntry(this)方法取值

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocal.ThreadLocalMap map = this.getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            this.createMap(t, value);
        }
    }


public T get() {
        Thread t = Thread.currentThread();
        ThreadLocal.ThreadLocalMap map = this.getMap(t);
        if (map != null) {
            ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                T result = e.value;
                return result;
            }
        }
        return this.setInitialValue();
    }

private T setInitialValue() {
        T value = this.initialValue();
        Thread t = Thread.currentThread();
        ThreadLocal.ThreadLocalMap map = this.getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            this.createMap(t, value);
        }

        if (this instanceof TerminatingThreadLocal) {
 TerminatingThreadLocal.register((TerminatingThreadLocal)this);
        }

        return value;
    }

protected T initialValue() {
        return null;
    }

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);
    }

结论

若第一次执行的操作是set则会创建ThreadLocalMap,并放入value,key是当前ThreadLocal的hashcode。

若第一次执行的操作是get也会创建ThreadLocalMap。key为当前的ThreadLocal的hashcode,value为null,并返回null。

ThreadLocalMap

Hash冲突

根据 key计算 hash值,如果出现冲突,则向后探测,当到哈希表末尾的时候再从0开始,直到找到一个合适的位置,这种算法也决定了 ThreadLocalMap不适合存储大量数据。

扩容

ThreadLocalMap初始大小为 16,加载因子为 2/3,当 size大于 threshold时,就会进行扩容。

扩容时,新建一个大小为原来数组长度的两倍的数组,然后遍历旧数组中的 entry并将其插入到新的hash数组中,在扩容的时候,会把 keynullEntryvalue值设置为 null,以便内存回收,减少内存泄漏问题。

总结

Threadlocal是什么?在堆内存中,每个线程对应一块工作内存,threadlocal就是工作内存的一小块内存。

Threadlocal有什么用?threadlocal用于存取线程独享数据,提高访问效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值