ThreadLocal原理及使用场景大揭秘( 二 )


  1. 再哈希法:如果hash出的index已经有值 , 就再hash , 不行继续hash , 直至找到空的index位置 。
  2. **开放地址法:**如果hash出的index已经有值 , 通过算法在它前面或后面的若干位置寻找空位 。
  3. 建立公共溢出区: 把冲突的hash值放到另外一块溢出区 。
  4. 链式地址法: 把产生hash冲突的hash值以链表形式存储在index位置上 。HashMap的解决方案 。
ThreadLocalMap用的是开放地址方法 , 如果当前位置有值 , 就继续寻找下一个位置 , 注意table[len-1]的下一个位置是table[0] , 就像是一个环形数组 , 所以也叫闭散列法 。如果一直都找不到空位置就会出现死循环 , 发生内存溢出 。当然有扩容机制 , 一般不会找不到空位置的 。
/*** ThreadLocalMap is a customized hash map suitable only for* maintaining thread local values. No operations are exported* outside of the ThreadLocal class. The class is package private to* allow declaration of fields in class Thread.  To help deal with* very large and long-lived usages, the hash table entries use* WeakReferences for keys. However, since reference queues are not* used, stale entries are guaranteed to be removed only when* the table starts running out of space.* */static class ThreadLocalMap {       static class Entry extends WeakReference<ThreadLocal<?>> {            /** The value associated with this ThreadLocal. */            Object value;      Entry(ThreadLocal<?> k, Object v) {          super(k);          value = v;        }   }     /**   * The table, resized as necessary.   * table.length MUST always be a power of two.   * table.length必须是2的幂次   */   private Entry[] table;  /**   * The number of entries in the table.   * table实际已经存放#Entry的数量  */    private int size = 0;   /**    * The next size value at which to resize.    * table扩容的阈值 , 初始threshold = length * 2 / 3 , 当size>threshold*3/4时就扩容    */    private int threshold;      /**    * The initial capacity -- MUST be a power of two.    * table 的默认容量    */    private static final int INITIAL_CAPACITY = 16;      /**    * Set the value associated with key.    *    * @param key the thread local object    * @param value the value to be set    */    private void set(ThreadLocal<?> key, Object value) {            Entry[] tab = table;            int len = tab.length;            //计算key的角标index 。就是用key的threadLocalHashCode & (len-1)等效于key.threadLocalHashCode%len            //只是&要比%效率高 , 它们之所以可以等效 , 因为len是2的n次幂 。            //threadLocalHashCode并不影响读懂这块代码 , 放在后面详说            int i = key.threadLocalHashCode & (len-1);            //开放地址方法 , 循环tab            for (Entry e = tab[i];                 e != null;                 e = tab[i = nextIndex(i, len)]) {                ThreadLocal<?> k = e.get();                //key相同 , 更新value                if (k == key) {                    e.value = value;                    return;                }                //key为空 , 说明ThreadLocal实例被回收了 , 用新key-value替代                if (k == null) {                    replaceStaleEntry(key, value, i);                    return;                }            }            //table[i]=null 新建一个Entity , ++size            tab[i] = new Entry(key, value);            int sz = ++size;            if (!cleanSomeSlots(i, sz) && sz >= threshold)                rehash();        }    //整理table   private void rehash() {      //删除table[]陈旧元素      expungeStaleEntries();      //size依然大于3/4 threshold , 扩容      if (size >= threshold - threshold / 4)          resize();    }/** * Expunge all stale entries in the table. * 删除table[]所有key==null的entity*/  private void expungeStaleEntries() {    Entry[] tab = table;     int len = tab.length;      for (int j = 0; j < len; j++) {       Entry e = tab[j];        if (e != null && e.get() == null)           expungeStaleEntry(j);        }  }      /**     * Double the capacity of the table.     * 扩容为原数组的2倍    */   private void resize() {            Entry[] oldTab = table;            int oldLen = oldTab.length;            int newLen = oldLen * 2;            //创建2倍容量的新数组            Entry[] newTab = new Entry[newLen];            int count = 0;            for (int j = 0; j < oldLen; ++j) {                Entry e = oldTab[j];                if (e != null) {                    //如果线程的                    ThreadLocal<?> k = e.get();                    if (k == null) {                        e.value = null; // Help the GC                    } else {                         //计算新数组index                        int h = k.threadLocalHashCode & (newLen - 1);                        while (newTab[h] != null)                            h = nextIndex(h, newLen);                        newTab[h] = e;                        count++;                    }                }            }            setThreshold(newLen);            size = count;            table = newTab;   }   //返回当前线程对应的ThreadLocalMap   ThreadLocalMap getMap(Thread t) {        return t.threadLocals;   }}


推荐阅读