【源码学习】ThreadLocal

大概介绍

ThreadLocal通俗说法:线程的本地变量,通过为每个线程创建副本的方式解决线程隔离问题,实现线程中的变量传递
比如:

  • 一个老项目,要从单线程的改为多线程实现了,那么对于一些变量,不是线程共享的,就要用ThreadLocal包装起来
  • 每个线程对应一个请求连接,在这个线程中的多个类、方法都要用到这个请求的用户信息,可见这个信息不是在线程间共享的,那么用ThreadLocal把用户信息包装起来

ThreadLocalMap

  • ThreadLocalMap是ThreadLocal的一个内部静态类,Thread类里面有两个ThreadLocal.ThreadLocalMap类型的内部对象,分别为threadLocals、inheritableThreadLocals。threadLocals是线程独占的变量,子线程可以读到父线程inheritableThreadLocals变量的值。
  • ThreadLocalMap维护着一堆key是ThreadLocal类型,value随便的Entry。也就是说,Thread类的threadLocals对象有一个内部的Entry[]类型的table变量,table的每一个Entry的key是ThreadLocal对象,value随便。
  • ThreadLocal<?>类型的成员是一个弱引用,其特点是,当引用元素无强引用时,JVM GC时会立即回收引用元素。划重点!!!这里,知道弱引用怎么写了吗?!!!!
static class ThreadLocalMap {
	static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    private Entry[] table;
    // 初始化ThreadLocalMap
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
      table = new Entry[INITIAL_CAPACITY];
       int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
       table[i] = new Entry(firstKey, firstValue);
       size = 1;
       setThreshold(INITIAL_CAPACITY);
   }
}

方法

public T get();
public void set(T value);
public void remove();
protected T initialValue();
1. initialValue方法

默认的initialValue是返回null,你也可以在创建ThreadLocal变量的时候重写该函数。

// 默认的
protected T initialValue() {
        return null;
    }
// 重写initialValue函数的实现
private static final ThreadLocal<Map<Charset, CharsetDecoder>> decoders =
    new ThreadLocal<Map<Charset, CharsetDecoder>>()
    {
        @Override
        protected Map<Charset, CharsetDecoder> initialValue()
        {
            return new IdentityHashMap<>();
        }
    };
2. get方法

首先获取当前的线程,然后获取当前线程的threadLocals,也就是线程独占变量,通过map.getEntry(this)获取Entry,这里的this代表一个ThreadLocal对象,即entry的key,返回这个entry的value。

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}
3. set方法

这个同get,不解释了

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

内存泄漏

ThreadLocalMap的key ThreadLocal是弱引用的原因:

  1. 如果使用强引用,则key永远都不会被回收,生命周期和线程一样
  2. 使用强引用,一旦key被回收,则就只剩ThreadLocalMap中的弱引用了,会在下次gc的时候被回收。
  3. 如果key被回收了,那么对应的value没有被回收,为了避免因此造成的内存泄漏,每次get()、set()、remove() ThreadLocalMap中的值的时候,会自动清理key为null的value。
  4. 一个比较好的做法是,在不需要的时候,手动remove掉。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值