【源码学习】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掉。

猜你喜欢

转载自blog.csdn.net/u010659877/article/details/109024380