解析ThreadLocal的源码

ThreadLocal在多线程中为每一个线程提供了变量副本,这样子每一个线程都可以在自己的工作空间内使用该副本了。

ThreadLocal的四个常见方法:
 public T get() 
 private T setInitialValue()
 public void set(T value)
 public void remove()

ThreadLocal使用的数据结构是ThreadLocalMap(本质就是map),首先先看一下ThreadLocalMap的源码:

static class Entry extends WeakReference<ThreadLocal<?>> {
    //Entry类是弱引用类型的
            /** The value associated with this ThreadLocal. */
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
 private static final int INITIAL_CAPACITY = 16;//初始容量16
 private Entry[] table;
 private int size = 0;
  private int threshold;
  private void setThreshold(int len) {
            threshold = len * 2 / 3;//装载因子2/3
        }
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
           //构造函数
            table = new Entry[INITIAL_CAPACITY];//初始一个长度为INITIAL_CAPACITY的表
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);//计算元素存放的位置
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
 private ThreadLocalMap(ThreadLocalMap parentMap) {
 //私有构造方法 
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);//用开放定址法 如果有冲突 则寻找下一个
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

看完ThreadLocalMap的源代码,接下来看一下ThreadLocal几个常用方法。

 public T get() {
        Thread t = Thread.currentThread();//获取当前线程获取key
        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();//获取值
    }
  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;
    }
 public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
 public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

补充:
内存泄漏问题(参考其他博文)
  在上面提到过,每个thread中都存在一个map, map的类型是ThreadLocal.ThreadLocalMap. Map中的key为一个threadlocal实例. 这个Map的确使用了弱引用,不过弱引用只是针对key. 每个key都弱引用指向threadlocal. 当把threadlocal实例置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收. 但是,我们的value却不能回收,因为存在一条从current thread连接过来的强引用. 只有当前thread结束以后, current thread就不会存在栈中,强引用断开, Current Thread, Map, value将全部被GC回收.

所以得出一个结论就是只要这个线程对象被gc回收,就不会出现内存泄露,但在threadLocal设为null和线程结束这段时间不会被回收的,就发生了我们认为的内存泄露。其实这是一个对概念理解的不一致,也没什么好争论的。最要命的是线程对象不被回收的情况,这就发生了真正意义上的内存泄露。比如使用线程池的时候,线程结束是不会销毁的,会再次使用的。就可能出现内存泄露。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值