ThreadLocal

来源:互联网 发布:python哪个版本好用 编辑:程序博客网 时间:2024/05/22 12:21

首先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