线程-ThreadLocal

类结构:

ThreadLocal类中有个ThreadLocalMap内部静态类。内部静态类和外部类,可以单独存在,创建各自不会触发对方的初始化。Entry又是ThreadLocalMap的内部静态类,是虚引用的子类,意味着EntryThreadLocal的引用是弱引用,ThreadLocal对象的GC和Entry对象没有关系。

class ThreadLocal{
    static class ThreadLocalMap {
        static class Entry extends WeakReference> {
            Object value;
            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
    }}

通过ThreadLocal类中的getset方法的分析,方法中操作的map是从Thread类中获取的,ThreadLocal只是map中Entry对象中的key

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();
}
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
为啥是这里使用的弱引用?
ThreadLocal aa = new ThreadLocal<>();
aa.set("dfd");
String s = aa.get();
aa.remove();

举个简单使用ThreadLocal的例子,在执行set的时候,会在当前线程中创建一个ThreadLocalMap实例,EntryThreadLocal实例 -> "dfd")
这时候ThreadLocal的实例会被两个地方引用:

  • aa这个变量(强引用)
  • Entry(弱引用)

代码执行完了,第一个引用就可以GC了,但是第二个Entry中的引用不会消失,如果是强引用,ThreadLocal就不会被GC,会产生内存泄漏。
当Entry中的key- >ThreadLocal 被GC了,那么Entry中的value就没有意义存在了。这时候Thread->ThreadLocalMap->Entry->value的强引用关系还在,虽然已经获取不到value了,但是也不会被GC,所以需要手动remove去释放value这块儿内存。

一般多线程如何使用?

一般在多线程都会使用ThreadLocal的时候,ThreadLocal可以设置为静态变量,jvm唯一实例,因为每个线程会有自己的map,ThreadLocal只是个key,不会影响正常使用,还减少了ThreadLocal实例的创建。

为啥ThreadLocalMap是内部静态类?

因为内部静态类在创建,用的时候不用依赖外部类的实例。因为这个特性,可以用内部静态类来实现外部类的单例。
子类InheritableThreadLocal可以直接创建ThreadLocalMap类的实例而不用通过ThreadLocal的实例获取。

public class InheritableThreadLocal extends ThreadLocal {
    protected T childValue(T parentValue) {
        return parentValue;
    }
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

感觉从代码的角度来说ThreadLocalMap可以单独出来单独写一个类,这样梳理的时候比较简单。从逻辑的角度来说ThreadLocal是对外的线程本地概念的抽象,ThreadLocalMap是实现的内部逻辑,所以内部类比较合适。

你可能感兴趣的:(线程-ThreadLocal)