ThreadLocal

首先Thread类中持有了ThreadLocalMap类型的对象;

ThreadLocal是负责向当前线程的添加数据到ThreadLocalMap 或 从 ThreadLocalMap中取的数据;


TheadLocal类的方法:set(T value) ,get().,remove()

set源码

   public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

  ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

可以看出是从当前线程中获得 ThreadLocal.ThreadLocalMap对象;

如果map不为空则设置值,如果为空则创建map对象并设置值;

1.先来看创建map:

   void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

     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);
        }

private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT); 
    }
private static AtomicInteger nextHashCode = 
    new AtomicInteger();

nextHashCode是静态量,并且从0开始增加,因此每次new ThreadLocal对象,都会有新的threadLocalHashCode值;
构造函数中初始化了table变量,用来存储Entry数据的数组;

Entry是实现了WeakReference的类,用来存储ThreadLocal对象以及value(自己要存储的数据);

int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
这一句是  计算 数据存储在table数组中的位置;  意义就是对:数组的容量进行取余运算;

数组的容量一定要是2^n,这样用 (hashcode)&(2^n-1)来计算,正好就是取余运算(即取hashcode的低n-1位数代表位置);

关与HASH_INREMENT =0x61c88647; 是为了减少hash冲突,使数据在table中分布更均匀;

2.再来看map的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;
            int len = tab.length;
            //通过当前ThreadLocal对象的hashcode来获得存储位置
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal k = e.get();
                //判断当前位置中存储的ThreadLocal是否是同一个对象,如果是则覆盖value值;如果k不为空,且又不是同一个对象,
                //则要通过nextIndex方法找下一个位置;
                if (k == key) {
                    e.value = value;
                    return;
                }
                //因为ThreadLocal对象有可能被回收,导致k==null,因此调用replaceStaleEntry
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            //没有匹配的,就在i位置赋值一个新的对象;
            tab[i] = new Entry(key, value);
            int sz = ++size;
            //调用cleanSomeSlots()对table进行清理,如果没有任何Entry被清理,并且表的size超过了阈值,就会调用rehash()方法。
           if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
  private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }

//这是替换陈旧的entry(entry中的ThreadLocal对象为空的Entry);

  * As a side effect, this method expunges all stale entries in the
         * "run" containing the stale entry.  (A run is a sequence of entries
         * between two null slots.)

意思是删除所有的在两个Null位置之间的陈旧Entry

  private void replaceStaleEntry(ThreadLocal key, Object value,
                                       int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;
            Entry e;

            // Back up to check for prior stale entry in current run.
            // We clean out whole runs at a time to avoid continual
            // incremental rehashing due to garbage collector freeing
            // up refs in bunches (i.e., whenever the collector runs).
            int slotToExpunge = staleSlot;
            //找到紧挨着的上一个Entry不为空,且ThreadLocal引用为空的位置赋值给slotToExpunge
            for (int i = prevIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = prevIndex(i, len))
                if (e.get() == null)
                    slotToExpunge = i;

            // Find either the key or trailing null slot of run, whichever
            // occurs first
               //从下一个位置开始找Entry,如果是一样的ThreadLocal,则 staleSlot位置换成当前位置的Entry对象,
               //当前位置换成staleSlot对象;下一步就是删除过时的Entry对象;
              for (int i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal k = e.get();

                // If we find key, then we need to swap it
                // with the stale entry to maintain hash table order.
                // The newly stale slot, or any other stale slot
                // encountered above it, can then be sent to expungeStaleEntry
                // to remove or rehash all of the other entries in run.
                if (k == key) {
                    e.value = value;

                    tab[i] = tab[staleSlot];
                    tab[staleSlot] = e;

                    // Start expunge at preceding stale entry if it exists
                    if (slotToExpunge == staleSlot)
                        slotToExpunge = i;
                    cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
                    return;
                }

                // If we didn't find stale entry on backward scan, the
                // first stale entry seen while scanning for key is the
                // first still present in the run.
                if (k == null && slotToExpunge == staleSlot)
                    slotToExpunge = i;
            }

            // If key not found, put new entry in stale slot
            tab[staleSlot].value = null;
            tab[staleSlot] = new Entry(key, value);

            // If there are any other stale entries in run, expunge them
            if (slotToExpunge != staleSlot)
                cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
        }

ThreadLocal的get()方法:
    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();
    }

尝试去获得当前线程中的map,如果map不为空就从map中找到对应的值并且返回;

如果没有存放ThreadLocal对象,则调用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;
    }
protected T initialValue() {
        return null;
    }


initialValue()方法是需要我们实现的方法,通过它获得设置值;

下边就是向当前线程的ThreadLocalMap中设置值了;   

remove方法删除当前线程ThreadLocalMap中与ThreadLocal键值对应的值;

     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

总结:

ThreadLocal类是给线程中ThreadLocalMap设置值的, 如果你想设置多个值,就要new多个ThreadLocal对象给线程设置值;

另外要注意
















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值