ThreadLocal源码分析

这一篇之所以讲ThreadLocal,是因为之前在读Handler,Looper的源码过程(见http://maosidiaoxian.iteye.com/blog/1927735)中,看到了这个类,引起了我的兴趣。而后来发现JAVA1.6中的TheadLocal类,和我在android源码看到的这个ThreadLocal类代码是不一样的。所以这篇先讲一下Java的ThreadLocal。 
Java中ThreadLocal在Java1.2就已经提出了,后来重构过,所以我在想android中的这个类的实现是不是重构过之前的版本优化过的,由于未找到早前版本该类的代码,所以只能进行猜测。 

ThreadLocal用于提供线程局部变量,即通过这里的get()和set()方法访问某个变量的线程都有自己的局部变量。但在这里需要注意的是,它是通过各个线程自己创建对象然后调用这里的set方法设置进去,而不是由ThreadLocal自己来创建变量的副本的。就像上面链接的那一篇文章里面提到的android的Looper类所用的(虽然Android里ThreadLocal实现代码不一样,但用法是一样的),代码如下: 
Java代码   收藏代码
  1. static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();  
  2. private static void prepare(boolean quitAllowed) {  
  3.     if (sThreadLocal.get() != null) {  
  4.         throw new RuntimeException("Only one Looper may be created per thread");  
  5.     }  
  6.     sThreadLocal.set(new Looper(quitAllowed));  
  7. }  

可以看到,线程中的局部变量,是在线程中创建,然后调用ThreadLocal.set()方法设置进去的。 

下面详细读一下ThreadLocal这个类,看看它是怎么实现的(jdk1.6的源码)。 

 
从上图可以看到ThreadLocal有三个变(常)量,代码如下: 
Java代码   收藏代码
  1. /** 
  2.  * ThreadLocals rely on per-thread linear-probe hash maps attached 
  3.  * to each thread (Thread.threadLocals and 
  4.  * inheritableThreadLocals).  The ThreadLocal objects act as keys, 
  5.  * searched via threadLocalHashCode.  This is a custom hash code 
  6.  * (useful only within ThreadLocalMaps) that eliminates collisions 
  7.  * in the common case where consecutively constructed ThreadLocals 
  8.  * are used by the same threads, while remaining well-behaved in 
  9.  * less common cases. 
  10.  */  
  11. private final int threadLocalHashCode = nextHashCode();  
  12.   
  13. /** 
  14.  * The next hash code to be given out. Updated atomically. Starts at 
  15.  * zero. 
  16.  */  
  17. private static AtomicInteger nextHashCode =  
  18.     new AtomicInteger();  
  19.   
  20. /** 
  21.  * The difference between successively generated hash codes - turns 
  22.  * implicit sequential thread-local IDs into near-optimally spread 
  23.  * multiplicative hash values for power-of-two-sized tables. 
  24.  */  
  25. private static final int HASH_INCREMENT = 0x61c88647;  

其中HASH_INCREMENT 是一个常量,它表示连续的两个ThreadLocal实例的threadLocalHashCode值的增量。至于为什么用0x61c88647常量,我也没明白,只百度出一堆跟加密算法有关的内容。nextHashCode变量是AtomicInteger类型,AtomicInteger是一个提供原子操作的Integer类,简单而言就是不用Synchronized关键字也可以进行线程安全的加减操作。 

当创建一个ThreadLocal对象时,会进行以下操作: 
Java代码   收藏代码
  1. public ThreadLocal() {  
  2. }  
  3.   
  4. private final int threadLocalHashCode = nextHashCode();  

我们再来看nextHashCode()这个方法,代码如下: 
Java代码   收藏代码
  1. /** 
  2.  * Returns the next hash code. 
  3.  */  
  4. private static int nextHashCode() {  
  5.     return nextHashCode.getAndAdd(HASH_INCREMENT);  
  6. }  

它是返回一个hash code,同时对nextHashCode加上增量HASH_INCREMENT。也就是在初始化ThreadLocal的实例过程中,做的仅仅是将一个哈希值赋给实例的threadLocalHashCode,并生成下一个哈希值。而前面可以看到threadLocalHashCode是final的,在个类中,它用来区分不同的ThreadLocal对象。 

在前面的图当中,我们可以看到在ThreadLocal里还有一个ThreadLocalMap的内部类,从类名来看应该是被设计来保存线程局部变量的Map,但是在ThreadLocal类或它的实例当中,并没有其他变量,那么通过ThreadLocal.set()放进去的值又是怎么保存的呢? 
我们继续看ThreadLocal的set()和其他方法: 
Java代码   收藏代码
  1. /** 
  2.  * Sets the current thread's copy of this thread-local variable 
  3.  * to the specified value.  Most subclasses will have no need to 
  4.  * override this method, relying solely on the {@link #initialValue} 
  5.  * method to set the values of thread-locals. 
  6.  * 
  7.  * @param value the value to be stored in the current thread's copy of 
  8.  *        this thread-local. 
  9.  */  
  10. public void set(T value) {  
  11.     Thread t = Thread.currentThread();  
  12.     ThreadLocalMap map = getMap(t);  
  13.     if (map != null)  
  14.         map.set(this, value);  
  15.     else  
  16.         createMap(t, value);  
  17. }  
  18.   
  19. /** 
  20.  * Get the map associated with a ThreadLocal. Overridden in 
  21.  * InheritableThreadLocal. 
  22.  * 
  23.  * @param  t the current thread 
  24.  * @return the map 
  25.  */  
  26. ThreadLocalMap getMap(Thread t) {  
  27.     return t.threadLocals;  
  28. }  
  29.   
  30. /** 
  31.  * Create the map associated with a ThreadLocal. Overridden in 
  32.  * InheritableThreadLocal. 
  33.  * 
  34.  * @param t the current thread 
  35.  * @param firstValue value for the initial entry of the map 
  36.  * @param map the map to store. 
  37.  */  
  38. void createMap(Thread t, T firstValue) {  
  39.     t.threadLocals = new ThreadLocalMap(this, firstValue);  
  40. }  

从上面的代码可以看到,set()方法是调用ThreadLocalMap.set()方法保存到ThreadLocalMap实例当中,但是ThreadLocalMap实例却不是保存在Thread中,它通过getMap()方法去取,当取不到的时候就去创建,但是创建之后却是保存在Thread实例中。可以看到Thread.java的代码有如下声明: 
Java代码   收藏代码
  1. ThreadLocal.ThreadLocalMap threadLocals = null;  

这种设计很巧妙,线程中有各自的map,而把ThreadLocal实例作为key,这样除了当线程销毁时相关的线程局部变量被销毁之外,还让性能提升了很多。 

看完set()方法,再来看get()方法,它的代码如下: 
Java代码   收藏代码
  1. /** 
  2.  * Returns the value in the current thread's copy of this 
  3.  * thread-local variable.  If the variable has no value for the 
  4.  * current thread, it is first initialized to the value returned 
  5.  * by an invocation of the {@link #initialValue} method. 
  6.  * 
  7.  * @return the current thread's value of this thread-local 
  8.  */  
  9. public T get() {  
  10.     Thread t = Thread.currentThread();  
  11.     ThreadLocalMap map = getMap(t);  
  12.     if (map != null) {  
  13.         ThreadLocalMap.Entry e = map.getEntry(this);  
  14.         if (e != null)  
  15.             return (T)e.value;  
  16.     }  
  17.     return setInitialValue();  
  18. }  
  19.   
  20. /** 
  21.  * Variant of set() to establish initialValue. Used instead 
  22.  * of set() in case user has overridden the set() method. 
  23.  * 
  24.  * @return the initial value 
  25.  */  
  26. private T setInitialValue() {  
  27.     T value = initialValue();  
  28.     Thread t = Thread.currentThread();  
  29.     ThreadLocalMap map = getMap(t);  
  30.     if (map != null)  
  31.         map.set(this, value);  
  32.     else  
  33.         createMap(t, value);  
  34.     return value;  
  35. }  
  36.   
  37. /** 
  38.  * Returns the current thread's "initial value" for this 
  39.  * thread-local variable.  This method will be invoked the first 
  40.  * time a thread accesses the variable with the {@link #get} 
  41.  * method, unless the thread previously invoked the {@link #set} 
  42.  * method, in which case the <tt>initialValue</tt> method will not 
  43.  * be invoked for the thread.  Normally, this method is invoked at 
  44.  * most once per thread, but it may be invoked again in case of 
  45.  * subsequent invocations of {@link #remove} followed by {@link #get}. 
  46.  * 
  47.  * <p>This implementation simply returns <tt>null</tt>; if the 
  48.  * programmer desires thread-local variables to have an initial 
  49.  * value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be 
  50.  * subclassed, and this method overridden.  Typically, an 
  51.  * anonymous inner class will be used. 
  52.  * 
  53.  * @return the initial value for this thread-local 
  54.  */  
  55. protected T initialValue() {  
  56.     return null;  
  57. }  

可以看到在get()方法中,是通过当前线程获取map,再从map当中取得对象。如果取不到(如map为null或取到的值为null),则调用setInitialValue()方法对其设置初始值。setInitialValue()方法会调用initialValue()方法得到一个初始值(默认为null),然后当Thread中的map不为空时,把初始值设置进去,否则为它创建一个ThreadLocalMap对象(但不会调用map的set方法保存这个初始值),最后返回这个初始值。 

最后看remove()方法,它提供了移除此线程局部变量在当前进程的值。代码如下:
Java代码   收藏代码
  1. /** 
  2.  * Removes the current thread's value for this thread-local 
  3.  * variable.  If this thread-local variable is subsequently 
  4.  * {@linkplain #get read} by the current thread, its value will be 
  5.  * reinitialized by invoking its {@link #initialValue} method, 
  6.  * unless its value is {@linkplain #set set} by the current thread 
  7.  * in the interim.  This may result in multiple invocations of the 
  8.  * <tt>initialValue</tt> method in the current thread. 
  9.  * 
  10.  * @since 1.5 
  11.  */  
  12.  public void remove() {  
  13.      ThreadLocalMap m = getMap(Thread.currentThread());  
  14.      if (m != null)  
  15.          m.remove(this);  
  16.  }  


关于ThreadLocal就读到这里,有时间再写下它的内部类ThreadLocalMap。 


上篇讲到了ThreadLocal类(http://maosidiaoxian.iteye.com/blog/1939142),这篇继续讲ThreadLocal中的ThreadLocalMap内部类。 

下面先通过一张图,看一下这个内部类的结构: 
 
可以看到在ThreadLocalMap类中,有一个常量,三个成员变量,代码如下: 

Java代码   收藏代码
  1. /** 
  2.  * The initial capacity -- MUST be a power of two. 
  3.  */  
  4. private static final int INITIAL_CAPACITY = 16;  
  5.   
  6. /** 
  7.  * The table, resized as necessary. 
  8.  * table.length MUST always be a power of two. 
  9.  */  
  10. private Entry[] table;  
  11.   
  12. /** 
  13.  * The number of entries in the table. 
  14.  */  
  15. private int size = 0;  
  16.   
  17. /** 
  18.  * The next size value at which to resize. 
  19.  */  
  20. private int threshold; // Default to 0  

一个map对象是有一个容量的,INITIAL_CAPACITY 常量表示这个Map的默认的初始化容量。 
数组table是一个实体表,保存设置进去的对象,长度必须为2的n次方的值。它是一个Entry类型,Entry类是ThreadLocalMap的一个内部类,继承自弱引用类型WeakReference,定义如下: 
Java代码   收藏代码
  1. static class Entry extends WeakReference<ThreadLocal> {  
  2.     /** The value associated with this ThreadLocal. */  
  3.     Object value;  
  4.   
  5.     Entry(ThreadLocal k, Object v) {  
  6.         super(k);  
  7.         value = v;  
  8.     }  
  9. }  

size变量表示实体表里实体的大小,初始值为0;threshold表示表的阈值,默认为0,当实体表的保存的实体大于这个阈值时,就需要对实体表table调整大小了。 

现在来看一下ThreadLocalMap实例化的时候做了些什么。 
ThreadLcoalMap共有两个构造方法,代码如下: 
Java代码   收藏代码
  1. ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {  
  2.     table = new Entry[INITIAL_CAPACITY];  
  3.     int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);  
  4.     table[i] = new Entry(firstKey, firstValue);  
  5.     size = 1;  
  6.     setThreshold(INITIAL_CAPACITY);  
  7. }  
  8.   
  9. /** 
  10.  * Construct a new map including all Inheritable ThreadLocals 
  11.  * from given parent map. Called only by createInheritedMap. 
  12.  * 
  13.  * @param parentMap the map associated with parent thread. 
  14.  */  
  15. private ThreadLocalMap(ThreadLocalMap parentMap) {  
  16.     Entry[] parentTable = parentMap.table;  
  17.     int len = parentTable.length;  
  18.     setThreshold(len);  
  19.     table = new Entry[len];  
  20.   
  21.     for (int j = 0; j < len; j++) {  
  22.         Entry e = parentTable[j];  
  23.         if (e != null) {  
  24.             ThreadLocal key = e.get();  
  25.             if (key != null) {  
  26.                 Object value = key.childValue(e.value);  
  27.                 Entry c = new Entry(key, value);  
  28.                 int h = key.threadLocalHashCode & (len - 1);  
  29.                 while (table[h] != null)  
  30.                     h = nextIndex(h, len);  
  31.                 table[h] = c;  
  32.                 size++;  
  33.             }  
  34.         }  
  35.     }  
  36. }  

这两个构造方法,一个为default,一个为private。在上一篇文章提到的,在ThreadLocal里的set方法获取不到这个map,需要创建的时候,它调用ThreadLcoal里的createMap()方法,而createMap()方法则调用default的这个构造方法。而另一个构造方法是在ThreadLocal里的createInheritedMap()中调用的,这个方法会由Thread里的构造方法来调用(在Thread类里的init()方法来调用,而init()方法只被Thread里的构造方法调用),我们在这里就不看它了。 
分析一下ThreadLocalMap里带两个参数的这个构造方法,这两个参数为创建这个ThreadLocalMap对象之后的第一对键值对。在构造方法里,先对table进行初始化,默认大小为INITIAL_CAPACITY。然后计算第一对键值对要存放的位置,即在table中的下标,它的代码如下: 
Java代码   收藏代码
  1. int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);  

我们知道INITIAL_CAPACITY 是一个2的n次幂的值,在这里它为16,即2的4次方,那么INITIAL_CAPACITY - 1 用16进度表示也就是0xF。firstKey.threadLocalHashCode与它作位的与运算,也就是取了threadLocal的哈希值的低4位(当table扩大时,取的值的范围也会跟着增加,但肯定是不大于table的长度的,这一点在ThreadLocalMap里的set方法可以体现出来,而table的长度必须为2的n次也是这种计算方法的前提条件),并将其作为在表中的下标(table的长度也是)。然后为这个键值对在数组相应的下标下创建一个Entry对象,最后设置阈值。在这里它的阈值为table长度的2/3。 

在计算下标的时候,从前面的计算方法中我们可以想到一种情况,即两个ThreadLocal对象虽然他们哈希值不同,但是通过与运算计算出的下标正好相同。对这种情况,它会计算下一个坐标,而下一个坐标则通过当前坐标+1的方式取得,在nextIndex()方法可以看到,代码如下(顺便贴一下prevIndex()方法): 
Java代码   收藏代码
  1. /** 
  2.  * Increment i modulo len. 
  3.  */  
  4. private static int nextIndex(int i, int len) {  
  5.     return ((i + 1 < len) ? i + 1 : 0);  
  6. }  
  7.   
  8. /** 
  9.  * Decrement i modulo len. 
  10.  */  
  11. private static int prevIndex(int i, int len) {  
  12.     return ((i - 1 >= 0) ? i - 1 : len - 1);  
  13. }  


下面我们再来看一下set()方法和get()方法。 
先看set()方法,相关代码如下: 
Java代码   收藏代码
  1. /** 
  2.  * Set the value associated with key. 
  3.  * 
  4.  * @param key the thread local object 
  5.  * @param value the value to be set 
  6.  */  
  7. private void set(ThreadLocal key, Object value) {  
  8.   
  9.     // We don't use a fast path as with get() because it is at  
  10.     // least as common to use set() to create new entries as  
  11.     // it is to replace existing ones, in which case, a fast  
  12.     // path would fail more often than not.  
  13.   
  14.     Entry[] tab = table;  
  15.     int len = tab.length;  
  16.     int i = key.threadLocalHashCode & (len-1);  
  17.   
  18.     for (Entry e = tab[i];  
  19.          e != null;  
  20.          e = tab[i = nextIndex(i, len)]) {  
  21.         ThreadLocal k = e.get();  
  22.   
  23.         if (k == key) {  
  24.             e.value = value;  
  25.             return;  
  26.         }  
  27.   
  28.         if (k == null) {  
  29.             replaceStaleEntry(key, value, i);  
  30.             return;  
  31.         }  
  32.     }  
  33.   
  34.     tab[i] = new Entry(key, value);  
  35.     int sz = ++size;  
  36.     if (!cleanSomeSlots(i, sz) && sz >= threshold)  
  37.         rehash();  
  38. }  
  39.   
  40. /** 
  41.  * Replace a stale entry encountered during a set operation 
  42.  * with an entry for the specified key.  The value passed in 
  43.  * the value parameter is stored in the entry, whether or not 
  44.  * an entry already exists for the specified key. 
  45.  * 
  46.  * As a side effect, this method expunges all stale entries in the 
  47.  * "run" containing the stale entry.  (A run is a sequence of entries 
  48.  * between two null slots.) 
  49.  * 
  50.  * @param  key the key 
  51.  * @param  value the value to be associated with key 
  52.  * @param  staleSlot index of the first stale entry encountered while 
  53.  *         searching for key. 
  54.  */  
  55. private void replaceStaleEntry(ThreadLocal key, Object value,  
  56.                                int staleSlot) {  
  57.     Entry[] tab = table;  
  58.     int len = tab.length;  
  59.     Entry e;  
  60.   
  61.     // Back up to check for prior stale entry in current run.  
  62.     // We clean out whole runs at a time to avoid continual  
  63.     // incremental rehashing due to garbage collector freeing  
  64.     // up refs in bunches (i.e., whenever the collector runs).  
  65.     int slotToExpunge = staleSlot;  
  66.     for (int i = prevIndex(staleSlot, len);  
  67.          (e = tab[i]) != null;  
  68.          i = prevIndex(i, len))  
  69.         if (e.get() == null)  
  70.             slotToExpunge = i;  
  71.   
  72.     // Find either the key or trailing null slot of run, whichever  
  73.     // occurs first  
  74.     for (int i = nextIndex(staleSlot, len);  
  75.          (e = tab[i]) != null;  
  76.          i = nextIndex(i, len)) {  
  77.         ThreadLocal k = e.get();  
  78.   
  79.         // If we find key, then we need to swap it  
  80.         // with the stale entry to maintain hash table order.  
  81.         // The newly stale slot, or any other stale slot  
  82.         // encountered above it, can then be sent to expungeStaleEntry  
  83.         // to remove or rehash all of the other entries in run.  
  84.         if (k == key) {  
  85.             e.value = value;  
  86.   
  87.             tab[i] = tab[staleSlot];  
  88.             tab[staleSlot] = e;  
  89.   
  90.             // Start expunge at preceding stale entry if it exists  
  91.             if (slotToExpunge == staleSlot)  
  92.                 slotToExpunge = i;  
  93.             cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);  
  94.             return;  
  95.         }  
  96.   
  97.         // If we didn't find stale entry on backward scan, the  
  98.         // first stale entry seen while scanning for key is the  
  99.         // first still present in the run.  
  100.         if (k == null && slotToExpunge == staleSlot)  
  101.             slotToExpunge = i;  
  102.     }  
  103.   
  104.     // If key not found, put new entry in stale slot  
  105.     tab[staleSlot].value = null;  
  106.     tab[staleSlot] = new Entry(key, value);  
  107.   
  108.     // If there are any other stale entries in run, expunge them  
  109.     if (slotToExpunge != staleSlot)  
  110.         cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);  
  111. }  

在set()方法中,首先通过key(threadLocal)中的hashc值与table的总长度取模,然后根据取模后的值作为下标,找到table当中下标为此值的entry,判断该entry是否存在。如果存在,判断entry里的key与value如果与当前要保存的key与value相同的话就不保存直接返回。如果entry里的key为null的话,就替换为当前要保存的key与value。否则就是碰撞的情况了,这时就调用nextIndex()方法计算下一个坐标。 
如果计算后的坐标获取到的entry为null,就new一个Entry对象并保存进去,然后调用cleanSomeSlots()对table进行清理,如果没有任何Entry被清理,并且表的size超过了阈值,就会调用rehash()方法。 
cleanSomeSlots()会调用expungeStaleEntry清理陈旧过时的Entry。rehash则会调用expungeStaleEntries()方法清理所有的陈旧的Entry,然后在size大于阈值的3/4时调用resize()方法进行扩容。代码如下: 
Java代码   收藏代码
  1. /** 
  2.  * Expunge a stale entry by rehashing any possibly colliding entries 
  3.  * lying between staleSlot and the next null slot.  This also expunges 
  4.  * any other stale entries encountered before the trailing null.  See 
  5.  * Knuth, Section 6.4 
  6.  * 
  7.  * @param staleSlot index of slot known to have null key 
  8.  * @return the index of the next null slot after staleSlot 
  9.  * (all between staleSlot and this slot will have been checked 
  10.  * for expunging). 
  11.  */  
  12. private int expungeStaleEntry(int staleSlot) {  
  13.     Entry[] tab = table;  
  14.     int len = tab.length;  
  15.   
  16.     // expunge entry at staleSlot  
  17.     tab[staleSlot].value = null;  
  18.     tab[staleSlot] = null;  
  19.     size--;  
  20.   
  21.     // Rehash until we encounter null  
  22.     Entry e;  
  23.     int i;  
  24.     for (i = nextIndex(staleSlot, len);  
  25.          (e = tab[i]) != null;  
  26.          i = nextIndex(i, len)) {  
  27.         ThreadLocal k = e.get();  
  28.         if (k == null) {  
  29.             e.value = null;  
  30.             tab[i] = null;  
  31.             size--;  
  32.         } else {  
  33.             int h = k.threadLocalHashCode & (len - 1);  
  34.             if (h != i) {  
  35.                 tab[i] = null;  
  36.   
  37.                 // Unlike Knuth 6.4 Algorithm R, we must scan until  
  38.                 // null because multiple entries could have been stale.  
  39.                 while (tab[h] != null)  
  40.                     h = nextIndex(h, len);  
  41.                 tab[h] = e;  
  42.             }  
  43.         }  
  44.     }  
  45.     return i;  
  46. }  
  47.   
  48. /** 
  49.  * Heuristically scan some cells looking for stale entries. 
  50.  * This is invoked when either a new element is added, or 
  51.  * another stale one has been expunged. It performs a 
  52.  * logarithmic number of scans, as a balance between no 
  53.  * scanning (fast but retains garbage) and a number of scans 
  54.  * proportional to number of elements, that would find all 
  55.  * garbage but would cause some insertions to take O(n) time. 
  56.  * 
  57.  * @param i a position known NOT to hold a stale entry. The 
  58.  * scan starts at the element after i. 
  59.  * 
  60.  * @param n scan control: <tt>log2(n)</tt> cells are scanned, 
  61.  * unless a stale entry is found, in which case 
  62.  * <tt>log2(table.length)-1</tt> additional cells are scanned. 
  63.  * When called from insertions, this parameter is the number 
  64.  * of elements, but when from replaceStaleEntry, it is the 
  65.  * table length. (Note: all this could be changed to be either 
  66.  * more or less aggressive by weighting n instead of just 
  67.  * using straight log n. But this version is simple, fast, and 
  68.  * seems to work well.) 
  69.  * 
  70.  * @return true if any stale entries have been removed. 
  71.  */  
  72. private boolean cleanSomeSlots(int i, int n) {  
  73.     boolean removed = false;  
  74.     Entry[] tab = table;  
  75.     int len = tab.length;  
  76.     do {  
  77.         i = nextIndex(i, len);  
  78.         Entry e = tab[i];  
  79.         if (e != null && e.get() == null) {  
  80.             n = len;  
  81.             removed = true;  
  82.             i = expungeStaleEntry(i);  
  83.         }  
  84.     } while ( (n >>>= 1) != 0);  
  85.     return removed;  
  86. }  
  87.   
  88. /** 
  89.  * Re-pack and/or re-size the table. First scan the entire 
  90.  * table removing stale entries. If this doesn't sufficiently 
  91.  * shrink the size of the table, double the table size. 
  92.  */  
  93. private void rehash() {  
  94.     expungeStaleEntries();  
  95.   
  96.     // Use lower threshold for doubling to avoid hysteresis  
  97.     if (size >= threshold - threshold / 4)  
  98.         resize();  
  99. }  
  100.   
  101. /** 
  102.  * Double the capacity of the table. 
  103.  */  
  104. private void resize() {  
  105.     Entry[] oldTab = table;  
  106.     int oldLen = oldTab.length;  
  107.     int newLen = oldLen * 2;  
  108.     Entry[] newTab = new Entry[newLen];  
  109.     int count = 0;  
  110.   
  111.     for (int j = 0; j < oldLen; ++j) {  
  112.         Entry e = oldTab[j];  
  113.         if (e != null) {  
  114.             ThreadLocal k = e.get();  
  115.             if (k == null) {  
  116.                 e.value = null// Help the GC  
  117.             } else {  
  118.                 int h = k.threadLocalHashCode & (newLen - 1);  
  119.                 while (newTab[h] != null)  
  120.                     h = nextIndex(h, newLen);  
  121.                 newTab[h] = e;  
  122.                 count++;  
  123.             }  
  124.         }  
  125.     }  
  126.   
  127.     setThreshold(newLen);  
  128.     size = count;  
  129.     table = newTab;  
  130. }  
  131.   
  132. /** 
  133.  * Expunge all stale entries in the table. 
  134.  */  
  135. private void expungeStaleEntries() {  
  136.     Entry[] tab = table;  
  137.     int len = tab.length;  
  138.     for (int j = 0; j < len; j++) {  
  139.         Entry e = tab[j];  
  140.         if (e != null && e.get() == null)  
  141.             expungeStaleEntry(j);  
  142.     }  
  143. }  


再下来是get()方法。 
Java代码   收藏代码
  1. /** 
  2.  * Get the entry associated with key.  This method 
  3.  * itself handles only the fast path: a direct hit of existing 
  4.  * key. It otherwise relays to getEntryAfterMiss.  This is 
  5.  * designed to maximize performance for direct hits, in part 
  6.  * by making this method readily inlinable. 
  7.  * 
  8.  * @param  key the thread local object 
  9.  * @return the entry associated with key, or null if no such 
  10.  */  
  11. private Entry getEntry(ThreadLocal key) {  
  12.     int i = key.threadLocalHashCode & (table.length - 1);  
  13.     Entry e = table[i];  
  14.     if (e != null && e.get() == key)  
  15.         return e;  
  16.     else  
  17.         return getEntryAfterMiss(key, i, e);  
  18. }  
  19.   
  20. /** 
  21.  * Version of getEntry method for use when key is not found in 
  22.  * its direct hash slot. 
  23.  * 
  24.  * @param  key the thread local object 
  25.  * @param  i the table index for key's hash code 
  26.  * @param  e the entry at table[i] 
  27.  * @return the entry associated with key, or null if no such 
  28.  */  
  29. private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {  
  30.     Entry[] tab = table;  
  31.     int len = tab.length;  
  32.   
  33.     while (e != null) {  
  34.         ThreadLocal k = e.get();  
  35.         if (k == key)  
  36.             return e;  
  37.         if (k == null)  
  38.             expungeStaleEntry(i);  
  39.         else  
  40.             i = nextIndex(i, len);  
  41.         e = tab[i];  
  42.     }  
  43.     return null;  
  44. }  

getEntry()方法通过计算出的下标从table中取出entry,如果取得的entry为null或它的key值不相等,就调用getEntryAfterMiss()方法,否则返回。 
而在getEntryAfterMiss()是当通过key与table的长度取模得到的下标取得entry后,entry里没有该key时所调用的。这时,如果获取的entry为null,即没有保存,就直接返回null,否则进入循环不,计算下一个坐标并获取对应的entry,并且当key相等时(表明找到了之前保存的值)返回entry,或是entry为null时退出循环,并返回null。 

最后是remove()方法,这个就比较简单了,计算到下标后,如果取得的entry的key与ThreadLocal相同,就调用Entry的clear方法把弱引用设置为null,然后调用expungeStaleEntry对table进行清理。代码如下: 
Java代码   收藏代码
  1. /** 
  2.  * Remove the entry for key. 
  3.  */  
  4. private void remove(ThreadLocal key) {  
  5.     Entry[] tab = table;  
  6.     int len = tab.length;  
  7.     int i = key.threadLocalHashCode & (len-1);  
  8.     for (Entry e = tab[i];  
  9.          e != null;  
  10.          e = tab[i = nextIndex(i, len)]) {  
  11.         if (e.get() == key) {  
  12.             e.clear();  
  13.             expungeStaleEntry(i);  
  14.             return;  
  15.         }  
  16.     }  
  17. }  

总结 :



本文转自:http://maosidiaoxian.iteye.com/blog/1940093

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值