ThreadLocal原理解析

源码解析

ThreadLocal的内部主要通过ThreadLocalMap来实现的,我们先看这个Map

ThreadLocalMap

ThreadLocalMap并没有实现Map接口,也没有集成AbstractMap等类,而是纯纯的一个内部实现。

基本的内部字段如下:

/**
 * The initial capacity -- MUST be a power of two.

Map初始容量
 */
private static final int INITIAL_CAPACITY = 16;
 

/**
 * The table, resized as necessary.
 * table.length MUST always be a power of two.
 * Map内部Entry数组

*/
private Entry[] table;

/**
 * The number of entries in the table.

默认entry数量0
 */
private int size = 0;

/**
 * The next size value at which to resize.
   扩容阈值

*/
private int threshold; // Default to 0

 

再看set方法

private void set(ThreadLocal<?> key, Object value) {

   

Entry[] tab = table;
   
int len = tab.length;

//根据ThreadLocalhash值计算entry索引
    int i = key.threadLocalHashCode & (len-1);
    //
进入循环
    for (Entry e = tab[i];
         e !=
null;
         e = tab[i = nextIndex(i, len)]) {

//获取当前ThreadLocal对象
        ThreadLocal<?> k = e.get();
        //
如果传入的ThreadLoacal与当前对象相等,则修改value
       
if (k == key) {
            e.
value = value;
           
return;
        }
        //
如果当前循环的ThreadLocal对象为空,
        if (k == null) {

//替换无效的entry为新的entry
            replaceStaleEntry(key, value, i);
           
return;
        }
    }
    //
如果当前索引是空的,则直接复制到新的entry对象中
    tab[i] = new Entry(key, value);

//size+1
   
int sz = ++size;

//清理无效槽位,并判断是否大于阈值
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        //rehash
及扩容

rehash();
}

//扩容方法

private void rehash() {

//清理失效的entry
    expungeStaleEntries();

    //清理失效节点过后,如果entry数量大于等于条件,则扩容
   
if (size >= threshold - threshold / 4)
        resize();
}

          ThreadLocalMap并不想hashMap的数据结构一样,他是一个很单纯的Entry对象数组,entry中包含ThreadLocal对象和值,并没有next节点。

根据ThreadLocal对象寻找索引的计算和hashMap类似,也是hash值和容量-1做与运算。

         在ThreadLocalMap中存在多种清理无效entry节点的方法,因为Entry继承了WeakReference对象,WeakReference是弱引用的标志,如果系统触发了GC并且当前的没有强引用关联,则会回收这个弱引用,所以方法会多出很多清理无效节点的方法。

         扩容机制比较简单,容量扩充2倍,阈值根据新容量重新计算threshold = len * 2 / 3;

我们再看ThreadLocal的set方法:

public void set(T value) {

//获取当前线程
    Thread t = Thread.currentThread();
    //获取当前线程的ThreadLocalMap

ThreadLocalMap map = getMap(t);
    if (map != null)

//如果map不是空,则直接放入值
        map.set(this, value);
    else

//创建map
       
createMap(t, value);
}

void createMap(Thread t, T firstValue) {

//new一个对象,并赋值给线程对象中的threadLocals字段中
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

         可以得知,ThreadLocal是通过Thread对象中的threadLocals字段来实现的,所谓的线程本地变量,都是有Thread来控制,Thread获取当前线程,Thread包含当前的ThreadLocalMap对象,支持ThreadLocalMap实现线程本地变量。

使用ThreadLocal注意问题:

  • 大部分基于web容器运行的实例,都会存在线程池,比如tomcat,每次请求会从线程池中拿取线程进行请求,此时,ThreadLocal使用后必须进行清除,否则线程重用导致数据问题
  • 内存泄漏,使用过后一定要释放,否则长期驻留内存,虽然ThreadLocal的key是弱引用,但是会增加垃圾回收Value的时间,并且强引用关联,是无法回收的。 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值