多线程总结(二) ---- ThreadLocal

一、代码示例

  可以看出不同线程之间 threadLocal 相互独立,互不干扰。

 1 public class ThreadLocalTest {
 2 
 3     public static void main(String[] args) {
 4         
 5         Thread t1 = new Thread(new MyRunnable());
 6         Thread t2 = new Thread(new MyRunnable());
 7         t1.start();
 8         t2.start();
 9         
10     }
11 
12     public static class MyRunnable implements Runnable {
13 
14         private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
15 
16         @Override
17         public void run() {
18             threadLocal.set((int) (Math.random() * 100D));
19             System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());
20         }
21     }
22 
23 }

二、源码解读

get()方法

    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);     // 当前线程当做key值,去取value 
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

set方法:

 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);  // 放入map,key值当前对象
15         else
16             createMap(t, value);
17     }

getMap和createMap方法显示该map类型 ThreadLocalMap

 1     /**
 2      * Get the map associated with a ThreadLocal. Overridden in
 3      * InheritableThreadLocal.
 4      *
 5      * @param  t the current thread
 6      * @return the map
 7      */
 8     ThreadLocalMap getMap(Thread t) {
 9         return t.threadLocals;
10     }
11 
12     /**
13      * Create the map associated with a ThreadLocal. Overridden in
14      * InheritableThreadLocal.
15      *
16      * @param t the current thread
17      * @param firstValue value for the initial entry of the map
18      */
19     void createMap(Thread t, T firstValue) {
20         t.threadLocals = new ThreadLocalMap(this, firstValue);
21     }

ThreadLocalMap 详细定义如下,不展开描述: 有独立的hashcode计算方法

  1 static class ThreadLocalMap {
  2 
  3         /**
  4          * The entries in this hash map extend WeakReference, using
  5          * its main ref field as the key (which is always a
  6          * ThreadLocal object).  Note that null keys (i.e. entry.get()
  7          * == null) mean that the key is no longer referenced, so the
  8          * entry can be expunged from table.  Such entries are referred to
  9          * as "stale entries" in the code that follows.
 10          */
 11         static class Entry extends WeakReference<ThreadLocal<?>> {
 12             /** The value associated with this ThreadLocal. */
 13             Object value;
 14 
 15             Entry(ThreadLocal<?> k, Object v) {
 16                 super(k);
 17                 value = v;
 18             }
 19         }
 20 
 21         /**
 22          * The initial capacity -- MUST be a power of two.
 23          */
 24         private static final int INITIAL_CAPACITY = 16;
 25 
 26         /**
 27          * The table, resized as necessary.
 28          * table.length MUST always be a power of two.
 29          */
 30         private Entry[] table;
 31 
 32         /**
 33          * The number of entries in the table.
 34          */
 35         private int size = 0;
 36 
 37         /**
 38          * The next size value at which to resize.
 39          */
 40         private int threshold; // Default to 0
 41 
 42         /**
 43          * Set the resize threshold to maintain at worst a 2/3 load factor.
 44          */
 45         private void setThreshold(int len) {
 46             threshold = len * 2 / 3;
 47         }
 48 
 49         /**
 50          * Increment i modulo len.
 51          */
 52         private static int nextIndex(int i, int len) {
 53             return ((i + 1 < len) ? i + 1 : 0);
 54         }
 55 
 56         /**
 57          * Decrement i modulo len.
 58          */
 59         private static int prevIndex(int i, int len) {
 60             return ((i - 1 >= 0) ? i - 1 : len - 1);
 61         }
 62 
 63         /**
 64          * Construct a new map initially containing (firstKey, firstValue).
 65          * ThreadLocalMaps are constructed lazily, so we only create
 66          * one when we have at least one entry to put in it.
 67          */
 68         ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
 69             table = new Entry[INITIAL_CAPACITY];
 70             int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
 71             table[i] = new Entry(firstKey, firstValue);
 72             size = 1;
 73             setThreshold(INITIAL_CAPACITY);
 74         }
 75 
 76         /**
 77          * Construct a new map including all Inheritable ThreadLocals
 78          * from given parent map. Called only by createInheritedMap.
 79          *
 80          * @param parentMap the map associated with parent thread.
 81          */
 82         private ThreadLocalMap(ThreadLocalMap parentMap) {
 83             Entry[] parentTable = parentMap.table;
 84             int len = parentTable.length;
 85             setThreshold(len);
 86             table = new Entry[len];
 87 
 88             for (int j = 0; j < len; j++) {
 89                 Entry e = parentTable[j];
 90                 if (e != null) {
 91                     @SuppressWarnings("unchecked")
 92                     ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
 93                     if (key != null) {
 94                         Object value = key.childValue(e.value);
 95                         Entry c = new Entry(key, value);
 96                         int h = key.threadLocalHashCode & (len - 1);
 97                         while (table[h] != null)
 98                             h = nextIndex(h, len);
 99                         table[h] = c;
100                         size++;
101                     }
102                 }
103             }
104         }
105 
106         /**
107          * Get the entry associated with key.  This method
108          * itself handles only the fast path: a direct hit of existing
109          * key. It otherwise relays to getEntryAfterMiss.  This is
110          * designed to maximize performance for direct hits, in part
111          * by making this method readily inlinable.
112          *
113          * @param  key the thread local object
114          * @return the entry associated with key, or null if no such
115          */
116         private Entry getEntry(ThreadLocal<?> key) {
117             int i = key.threadLocalHashCode & (table.length - 1);
118             Entry e = table[i];
119             if (e != null && e.get() == key)
120                 return e;
121             else
122                 return getEntryAfterMiss(key, i, e);
123         }
124 
125         /**
126          * Version of getEntry method for use when key is not found in
127          * its direct hash slot.
128          *
129          * @param  key the thread local object
130          * @param  i the table index for key's hash code
131          * @param  e the entry at table[i]
132          * @return the entry associated with key, or null if no such
133          */
134         private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
135             Entry[] tab = table;
136             int len = tab.length;
137 
138             while (e != null) {
139                 ThreadLocal<?> k = e.get();
140                 if (k == key)
141                     return e;
142                 if (k == null)
143                     expungeStaleEntry(i);
144                 else
145                     i = nextIndex(i, len);
146                 e = tab[i];
147             }
148             return null;
149         }
150 
151         /**
152          * Set the value associated with key.
153          *
154          * @param key the thread local object
155          * @param value the value to be set
156          */
157         private void set(ThreadLocal<?> key, Object value) {
158 
159             // We don't use a fast path as with get() because it is at
160             // least as common to use set() to create new entries as
161             // it is to replace existing ones, in which case, a fast
162             // path would fail more often than not.
163 
164             Entry[] tab = table;
165             int len = tab.length;
166             int i = key.threadLocalHashCode & (len-1);
167 
168             for (Entry e = tab[i];
169                  e != null;
170                  e = tab[i = nextIndex(i, len)]) {
171                 ThreadLocal<?> k = e.get();
172 
173                 if (k == key) {
174                     e.value = value;
175                     return;
176                 }
177 
178                 if (k == null) {
179                     replaceStaleEntry(key, value, i);
180                     return;
181                 }
182             }
183 
184             tab[i] = new Entry(key, value);
185             int sz = ++size;
186             if (!cleanSomeSlots(i, sz) && sz >= threshold)
187                 rehash();
188         }
189 
190         /**
191          * Remove the entry for key.
192          */
193         private void remove(ThreadLocal<?> key) {
194             Entry[] tab = table;
195             int len = tab.length;
196             int i = key.threadLocalHashCode & (len-1);
197             for (Entry e = tab[i];
198                  e != null;
199                  e = tab[i = nextIndex(i, len)]) {
200                 if (e.get() == key) {
201                     e.clear();
202                     expungeStaleEntry(i);
203                     return;
204                 }
205             }
206         }
207 
208         /**
209          * Replace a stale entry encountered during a set operation
210          * with an entry for the specified key.  The value passed in
211          * the value parameter is stored in the entry, whether or not
212          * an entry already exists for the specified key.
213          *
214          * As a side effect, this method expunges all stale entries in the
215          * "run" containing the stale entry.  (A run is a sequence of entries
216          * between two null slots.)
217          *
218          * @param  key the key
219          * @param  value the value to be associated with key
220          * @param  staleSlot index of the first stale entry encountered while
221          *         searching for key.
222          */
223         private void replaceStaleEntry(ThreadLocal<?> key, Object value,
224                                        int staleSlot) {
225             Entry[] tab = table;
226             int len = tab.length;
227             Entry e;
228 
229             // Back up to check for prior stale entry in current run.
230             // We clean out whole runs at a time to avoid continual
231             // incremental rehashing due to garbage collector freeing
232             // up refs in bunches (i.e., whenever the collector runs).
233             int slotToExpunge = staleSlot;
234             for (int i = prevIndex(staleSlot, len);
235                  (e = tab[i]) != null;
236                  i = prevIndex(i, len))
237                 if (e.get() == null)
238                     slotToExpunge = i;
239 
240             // Find either the key or trailing null slot of run, whichever
241             // occurs first
242             for (int i = nextIndex(staleSlot, len);
243                  (e = tab[i]) != null;
244                  i = nextIndex(i, len)) {
245                 ThreadLocal<?> k = e.get();
246 
247                 // If we find key, then we need to swap it
248                 // with the stale entry to maintain hash table order.
249                 // The newly stale slot, or any other stale slot
250                 // encountered above it, can then be sent to expungeStaleEntry
251                 // to remove or rehash all of the other entries in run.
252                 if (k == key) {
253                     e.value = value;
254 
255                     tab[i] = tab[staleSlot];
256                     tab[staleSlot] = e;
257 
258                     // Start expunge at preceding stale entry if it exists
259                     if (slotToExpunge == staleSlot)
260                         slotToExpunge = i;
261                     cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
262                     return;
263                 }
264 
265                 // If we didn't find stale entry on backward scan, the
266                 // first stale entry seen while scanning for key is the
267                 // first still present in the run.
268                 if (k == null && slotToExpunge == staleSlot)
269                     slotToExpunge = i;
270             }
271 
272             // If key not found, put new entry in stale slot
273             tab[staleSlot].value = null;
274             tab[staleSlot] = new Entry(key, value);
275 
276             // If there are any other stale entries in run, expunge them
277             if (slotToExpunge != staleSlot)
278                 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
279         }
280 
281         /**
282          * Expunge a stale entry by rehashing any possibly colliding entries
283          * lying between staleSlot and the next null slot.  This also expunges
284          * any other stale entries encountered before the trailing null.  See
285          * Knuth, Section 6.4
286          *
287          * @param staleSlot index of slot known to have null key
288          * @return the index of the next null slot after staleSlot
289          * (all between staleSlot and this slot will have been checked
290          * for expunging).
291          */
292         private int expungeStaleEntry(int staleSlot) {
293             Entry[] tab = table;
294             int len = tab.length;
295 
296             // expunge entry at staleSlot
297             tab[staleSlot].value = null;
298             tab[staleSlot] = null;
299             size--;
300 
301             // Rehash until we encounter null
302             Entry e;
303             int i;
304             for (i = nextIndex(staleSlot, len);
305                  (e = tab[i]) != null;
306                  i = nextIndex(i, len)) {
307                 ThreadLocal<?> k = e.get();
308                 if (k == null) {
309                     e.value = null;
310                     tab[i] = null;
311                     size--;
312                 } else {
313                     int h = k.threadLocalHashCode & (len - 1);
314                     if (h != i) {
315                         tab[i] = null;
316 
317                         // Unlike Knuth 6.4 Algorithm R, we must scan until
318                         // null because multiple entries could have been stale.
319                         while (tab[h] != null)
320                             h = nextIndex(h, len);
321                         tab[h] = e;
322                     }
323                 }
324             }
325             return i;
326         }
327 
328         /**
329          * Heuristically scan some cells looking for stale entries.
330          * This is invoked when either a new element is added, or
331          * another stale one has been expunged. It performs a
332          * logarithmic number of scans, as a balance between no
333          * scanning (fast but retains garbage) and a number of scans
334          * proportional to number of elements, that would find all
335          * garbage but would cause some insertions to take O(n) time.
336          *
337          * @param i a position known NOT to hold a stale entry. The
338          * scan starts at the element after i.
339          *
340          * @param n scan control: {@code log2(n)} cells are scanned,
341          * unless a stale entry is found, in which case
342          * {@code log2(table.length)-1} additional cells are scanned.
343          * When called from insertions, this parameter is the number
344          * of elements, but when from replaceStaleEntry, it is the
345          * table length. (Note: all this could be changed to be either
346          * more or less aggressive by weighting n instead of just
347          * using straight log n. But this version is simple, fast, and
348          * seems to work well.)
349          *
350          * @return true if any stale entries have been removed.
351          */
352         private boolean cleanSomeSlots(int i, int n) {
353             boolean removed = false;
354             Entry[] tab = table;
355             int len = tab.length;
356             do {
357                 i = nextIndex(i, len);
358                 Entry e = tab[i];
359                 if (e != null && e.get() == null) {
360                     n = len;
361                     removed = true;
362                     i = expungeStaleEntry(i);
363                 }
364             } while ( (n >>>= 1) != 0);
365             return removed;
366         }
367 
368         /**
369          * Re-pack and/or re-size the table. First scan the entire
370          * table removing stale entries. If this doesn't sufficiently
371          * shrink the size of the table, double the table size.
372          */
373         private void rehash() {
374             expungeStaleEntries();
375 
376             // Use lower threshold for doubling to avoid hysteresis
377             if (size >= threshold - threshold / 4)
378                 resize();
379         }
380 
381         /**
382          * Double the capacity of the table.
383          */
384         private void resize() {
385             Entry[] oldTab = table;
386             int oldLen = oldTab.length;
387             int newLen = oldLen * 2;
388             Entry[] newTab = new Entry[newLen];
389             int count = 0;
390 
391             for (int j = 0; j < oldLen; ++j) {
392                 Entry e = oldTab[j];
393                 if (e != null) {
394                     ThreadLocal<?> k = e.get();
395                     if (k == null) {
396                         e.value = null; // Help the GC
397                     } else {
398                         int h = k.threadLocalHashCode & (newLen - 1);
399                         while (newTab[h] != null)
400                             h = nextIndex(h, newLen);
401                         newTab[h] = e;
402                         count++;
403                     }
404                 }
405             }
406 
407             setThreshold(newLen);
408             size = count;
409             table = newTab;
410         }
411 
412         /**
413          * Expunge all stale entries in the table.
414          */
415         private void expungeStaleEntries() {
416             Entry[] tab = table;
417             int len = tab.length;
418             for (int j = 0; j < len; j++) {
419                 Entry e = tab[j];
420                 if (e != null && e.get() == null)
421                     expungeStaleEntry(j);
422             }
423         }
424     }
View Code

猜你喜欢

转载自www.cnblogs.com/clarino/p/11717345.html