ThreadLocal详解

ThreadLocal详解

TheadLocal 被叫做线程局部变量或者线程局部存储,作用就是将多个线程共享的变量拷贝一份到当前线程中使用,实质上就是线程中的局部变量,只不过这个变量的作用域是线程的作用域。

ThreadLocal类中只有五个方法

修饰符和返回值类型方法和描述
public Tget()获取到当前线程中局部变量的值
protected TinitialValue()返回此线程局部变量的初始值
public voidremove()删除此线程局部变量的值
public voidset()设置当前线程局部变量的值
public static <S> ThreadLocal <S>创建一个SuppliedThreadLocal对象

public void set(T value)
源码:

    public void set(T value) {
      Thread t = Thread.currentThread(); //获取当前线程
      ThreadLocalMap map = getMap(t); //获取到当前线程的ThreadLocalMap
      if (map != null) //如果map不为空  证明已经当前线程已经有局部变量了
          map.set(this, value); //将当前局部变脸存入map
      else
          createMap(t, value); //如果map为空证明第一次存值,则先创建map集合
  }

getMap()方法

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals; //在Thread类中有一个threadLocals属性存储的就是ThreadLocalMap
    }

如果map集合为空则执行createMap方法
源码:

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue); // 创建一个ThreadLocalMap对象初始化并且返回
    }

如果getMap获取到的map不为空 则调用ThreadLocalMap的set方法存值

        private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table; //在creatMap时 创建了初始容量为16的Entry数组
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1); //获取到数据存储下标

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get(); 获取到键值
	
                if (k == key) { // 将要设置的键值和获取到的进行比较 如果相同直接覆盖原有值
                    e.value = value;
                    return;
                }

                if (k == null) { //如果k为null则证明键值被回收了内存泄漏
                    replaceStaleEntry(key, value, i);// 替换过期的键值
                    return;
                }
            }
            //执行到这的条件是又创建了一个ThreadLocal对象然后存值 会形成条件k!=key k!=null 则会先执行i = nextIndex(i, len) 
            //然后e==null 直接执行下面的代码 直接创建一个Entry对象然后将值存入
			// 直接存值
            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

public T get()
源码:

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t); //获取到当前线程对象的ThreadLocalMap集合
        if (map != null) { //map不为空
            ThreadLocalMap.Entry e = map.getEntry(this); //根据当前的ThreadLocal对象获取到存值的Entry对象e
            if (e != null) { //e不为空
                @SuppressWarnings("unchecked")
                T result = (T)e.value; //直接取出value值
                return result; //返回结果
            }
        }
        return setInitialValue(); //如果map为空则给局部线程变量设置初始值
    }

ThreadLocalMap中的getEntry方法

        private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)//如果e.get()返回的值与key不相等
            //则证明查询的第二个线程局部变量 则执行getEntryAfterMiss方法
                return e;
            else
                return getEntryAfterMiss(key, i, e); 
        }

ThreadLocalMap中的getEntryAfterMiss方法

        private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) { 
                ThreadLocal<?> k = e.get();
                if (k == key) //如果校验成功是要查询的线程局部变量则返回Entry对象e
                    return e;
                if (k == null)
                    expungeStaleEntry(i);
                else //如果查询的是第二个线程局部变量则会执行到这 证明下标i 需要更新
                    i = nextIndex(i, len);
                e = tab[i]; //使用更新后的下标获取到新的Entry
            }
            return null;
        }

setInitialValue方法
该方法和ThreadLocal的set方法几乎一样,只是多个获取初始值这个步骤

    private T setInitialValue() {
        T value = initialValue(); //获取到初始化的值
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) //map不为空则调用map的set方法
            map.set(this, value);
        else
            createMap(t, value); //创建map
        return value; //返回初始的值
    }

public void remove()

     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread()); //获取到当前线程对应的Map集合
         if (m != null) //不为空则删除数据
             m.remove(this); //执行Map集合的remove方法
     }

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(); //调用Entry中的clear方法
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

Entry是ThreadLocalMap的静态内部类 ThreadLocalMap又是ThreadLocal的静态内部类

//很明显Entyr继承了WeakReference<ThreadLocal<?>> 则证明Key被弱引用包装了
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k); //调用了父类Reference的构造方法
                value = v;
            }
        }

Reference的构造方法(super(k);)

    Reference(T referent) {
        this(referent, null); //调用了两个参数的构造方法
    }

    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent; //将键值k赋值给了属性referent
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }

搞清楚了键值最终被如何包装之后,再回到Entry的clear方法(这个clear方法实质上是继承父类Reference的)

//e.clear()最终调用了 Reference中的clear方法
    public void clear() {
        this.referent = null; //上面说了键值最终被赋值给了 referent 
        //现在将referent置为null则实现了清楚功能,键值为null则对应了value无法找到也就实现了清除功能
   	}

protected T initialValue()
这个方法设计的就是为了 让子类去重写 如果不重写默认返回的初始值就是null

    protected T initialValue() {
        return null;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值