ThreadLocal学习

1、threadLocal图解

ThreadLocal学习_第1张图片

  java.lang.ThreadLocal类实现了线程的本地存储。

ThreadLocal的内部实现:

  • ThreadLocal的内部实现包括一个类似HashMap的对象,这里称之ThreadLocalMap。

  • ThreadLocalMap的key会持有对ThreadLocal实例的弱引用(Weak Reference),value会引用具体存储的对象实例。

【强引用】:

1、threalLocal对象指向threalLocalMap中的key .

2、线程对象 指向 堆中的 threalLocalMap。

【弱引用】:

1、threalLocalMap 的key为 threadLocal对象,即threadLocal对象 指向key为弱引用。

部分源码如下: 

// ThreadLocal类中的get()方法: 
	// 代码逻辑:
	// 1、通过当前线程对象,获取线程对象中的成员变量threadLocals 即threadLocalMap。
	// 2、拿到threadLocalMap后,根据当前threadLocal对象作为key,获取value值。
	// 3、如果threadLocalMap为空,进行初始化
	public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

	// ThreadLocal类中的setInitialValue()方法: 设置ThreadLocalMap的初始化值。
	private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
	// ThreadLocal类中的initialValue()方法:ThreadLocalMap初始value为null
	protected T initialValue() {
        return null;
    }
	// ThreadLocal类中的createMap方法: 初始化ThreadLocalMap,即线程的threadLocals对象初始化。
	void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
	
	// ThreadLocalMap类的构造方法:存储用的是Entry数组。Entry的key为ThreadLocal对象。
	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);
    }

	// ThreadLocal类中的getMap方法:
	ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
	// ThreadLocal类中的remove方法:
	public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
	// ThreadLocalMap类中的remove方法:
	private void remove(ThreadLocal key) {
        Entry[] tab = table;
        int len = tab.length;
        int i = key.threadLocalHashCode & (len-1);
        for (Entry e = tab[i];
             e != null;
             e = tab[i = nextIndex(i, len)]) {
            if (e.get() == key) {
                e.clear();
                expungeStaleEntry(i);
                return;
            }
        }
    }

	// Thread类中的成员变量threadLocal即: 线程中的ThreadLocalMap
	ThreadLocal.ThreadLocalMap threadLocals = null;

 2、ThreadLocal原理

  • Thread类中一个成员变量,ThreadLocal.ThreadLocalMap threadLocals

  • ThreadLocal本身不存储数据,像是一个工具类,基于ThreadLocal去操作ThreadLocalMap

  • ThreadLocalMap本身就是基于Entrty[]实现的,因为一个线程被可以绑定多个ThreadLocalMap,这样一来,可能需要存储多个数据,所以采用Entrty[]的形式实现。

  • 每一个线程有自己独立的ThreadLocalMap, 再基于ThreadLocald对象本身作为key,对value进行存取。

  • ThreadLocalMap的key是一个弱引用。弱引用的特点:即便对象有弱引用,在GC时,也必须被回收。如果ThreadLocal对象为局部变量,即方法执行结束后,threadLocal对象强引用消失,如果key的引用是强引用,会导致threadLocal对象无法被回收如果threadLocal是静态成员变量,则不会存在这个问题

ThreadLocal内存泄露问题:

  • 如果threadLocal对象的引用丢失即:使用的threadLocal不是静态成员变量,而是局部变量),key因为弱引用被GC回收掉,如果同时线程还没有被回收,就会导致内存泄露,内存中的value无法被回收,同时也无法被获取到。

  • 解决方法:只需要在使用完毕threadLocal对象之后,及时的调用threadLocal.remove()方法,移除该threalLocal对象key的Entry即可。(说明:threadLocal.remove()是threadLocal对象的强引用,即在移除Entry前,threalLocal对象不会失效)

总结:

  • 线程中threadLocalMap(即threadLocals),是在第一次调用ThreadLocal的get或set方法时,才会创建。

  • 线程调用很深,但又不想传参时,可以使用threadLocals

你可能感兴趣的:(Java基础,学习)