码迷,mamicode.com
首页 > 其他好文 > 详细

ThreadLocal

时间:2016-08-18 23:02:21      阅读:178      评论:0      收藏:0      [点我收藏+]

标签:

Java中的ThreadLocal 可以看做以线程标识为key的Map,在多线程开发中使用非常方便。

使用示例

 1 class ThreadEnv {
 2     
 3     // 用匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
 4     private static ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
 5         @Override 
 6         protected Integer initialValue() {
 7             return 10;
 8         }
 9     };
10 
11     public int get() {
12         // 第一次get到的是初始值
13         int a = threadId.get();
14         a++;
15         threadId.set(a);
16         return a;
17     }
18 }
19 
20 public class ThreadLocalTest {
21 
22     public static void main(String[] args) {
23         ThreadEnv sn = new ThreadEnv();
24         new TestClient(sn);
25         new TestClient(sn);
26         new TestClient(sn);
27     }
28 
29     private static class TestClient extends Thread {
30         private ThreadEnv sn;
31 
32         public TestClient(ThreadEnv sn) {
33             this.sn = sn;
34             this.start();
35         }
36 
37         public void run() {
38             for (int i = 0; i < 3; i++) {
39                 System.out.println(Thread.currentThread()+ " >>> " + sn.get());
40             }
41         }
42     }
43 
44 }

运行结果

Thread[Thread-2,5,main] >>> 11
Thread[Thread-1,5,main] >>> 11
Thread[Thread-0,5,main] >>> 11
Thread[Thread-1,5,main] >>> 12
Thread[Thread-1,5,main] >>> 13
Thread[Thread-2,5,main] >>> 12
Thread[Thread-0,5,main] >>> 12
Thread[Thread-0,5,main] >>> 13
Thread[Thread-2,5,main] >>> 13

源码解析

技术分享
  1     public ThreadLocal() {
  2     }
  3 
  4     /**
  5      * Returns the value in the current thread‘s copy of this
  6      * thread-local variable.  If the variable has no value for the
  7      * current thread, it is first initialized to the value returned
  8      * by an invocation of the {@link #initialValue} method.
  9      *
 10      * @return the current thread‘s value of this thread-local
 11      */
 12     public T get() {
 13         Thread t = Thread.currentThread();
 14         ThreadLocalMap map = getMap(t);
 15         if (map != null) {
 16             ThreadLocalMap.Entry e = map.getEntry(this);
 17             if (e != null) {
 18                 @SuppressWarnings("unchecked")
 19                 T result = (T)e.value;
 20                 return result;
 21             }
 22         }
 23         return setInitialValue();
 24     }
 25 
 26     /**
 27      * Variant of set() to establish initialValue. Used instead
 28      * of set() in case user has overridden the set() method.
 29      *
 30      * @return the initial value
 31      */
 32     private T setInitialValue() {
 33         T value = initialValue();
 34         Thread t = Thread.currentThread();
 35         ThreadLocalMap map = getMap(t);
 36         if (map != null)
 37             map.set(this, value);
 38         else
 39             createMap(t, value);
 40         return value;
 41     }
 42 
 43     /**
 44      * Sets the current thread‘s copy of this thread-local variable
 45      * to the specified value.  Most subclasses will have no need to
 46      * override this method, relying solely on the {@link #initialValue}
 47      * method to set the values of thread-locals.
 48      *
 49      * @param value the value to be stored in the current thread‘s copy of
 50      *        this thread-local.
 51      */
 52     public void set(T value) {
 53         Thread t = Thread.currentThread();
 54         ThreadLocalMap map = getMap(t);
 55         if (map != null)
 56             map.set(this, value);
 57         else
 58             createMap(t, value);
 59     }
 60 
 61     /**
 62      * Removes the current thread‘s value for this thread-local
 63      * variable.  If this thread-local variable is subsequently
 64      * {@linkplain #get read} by the current thread, its value will be
 65      * reinitialized by invoking its {@link #initialValue} method,
 66      * unless its value is {@linkplain #set set} by the current thread
 67      * in the interim.  This may result in multiple invocations of the
 68      * {@code initialValue} method in the current thread.
 69      *
 70      * @since 1.5
 71      */
 72      public void remove() {
 73          ThreadLocalMap m = getMap(Thread.currentThread());
 74          if (m != null)
 75              m.remove(this);
 76      }
 77 
 78     /**
 79      * Get the map associated with a ThreadLocal. Overridden in
 80      * InheritableThreadLocal.
 81      *
 82      * @param  t the current thread
 83      * @return the map
 84      */
 85     ThreadLocalMap getMap(Thread t) {
 86         return t.threadLocals;
 87     }
 88 
 89     /**
 90      * Create the map associated with a ThreadLocal. Overridden in
 91      * InheritableThreadLocal.
 92      *
 93      * @param t the current thread
 94      * @param firstValue value for the initial entry of the map
 95      */
 96     void createMap(Thread t, T firstValue) {
 97         t.threadLocals = new ThreadLocalMap(this, firstValue);
 98     }
 99 
100     /**
101      * Factory method to create map of inherited thread locals.
102      * Designed to be called only from Thread constructor.
103      *
104      * @param  parentMap the map associated with parent thread
105      * @return a map containing the parent‘s inheritable bindings
106      */
107     static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
108         return new ThreadLocalMap(parentMap);
109     }
110 
111     /**
112      * Method childValue is visibly defined in subclass
113      * InheritableThreadLocal, but is internally defined here for the
114      * sake of providing createInheritedMap factory method without
115      * needing to subclass the map class in InheritableThreadLocal.
116      * This technique is preferable to the alternative of embedding
117      * instanceof tests in methods.
118      */
119     T childValue(T parentValue) {
120         throw new UnsupportedOperationException();
121     }
122 
123     /**
124      * An extension of ThreadLocal that obtains its initial value from
125      * the specified {@code Supplier}.
126      */
127     static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
128 
129         private final Supplier<? extends T> supplier;
130 
131         SuppliedThreadLocal(Supplier<? extends T> supplier) {
132             this.supplier = Objects.requireNonNull(supplier);
133         }
134 
135         @Override
136         protected T initialValue() {
137             return supplier.get();
138         }
139     }
140 
141     /**
142      * ThreadLocalMap is a customized hash map suitable only for
143      * maintaining thread local values. No operations are exported
144      * outside of the ThreadLocal class. The class is package private to
145      * allow declaration of fields in class Thread.  To help deal with
146      * very large and long-lived usages, the hash table entries use
147      * WeakReferences for keys. However, since reference queues are not
148      * used, stale entries are guaranteed to be removed only when
149      * the table starts running out of space.
150      */
151     static class ThreadLocalMap {
152 
153         /**
154          * The entries in this hash map extend WeakReference, using
155          * its main ref field as the key (which is always a
156          * ThreadLocal object).  Note that null keys (i.e. entry.get()
157          * == null) mean that the key is no longer referenced, so the
158          * entry can be expunged from table.  Such entries are referred to
159          * as "stale entries" in the code that follows.
160          */
161         static class Entry extends WeakReference<ThreadLocal<?>> {
162             /** The value associated with this ThreadLocal. */
163             Object value;
164 
165             Entry(ThreadLocal<?> k, Object v) {
166                 super(k);
167                 value = v;
168             }
169         }
170 
171         /**
172          * The initial capacity -- MUST be a power of two.
173          */
174         private static final int INITIAL_CAPACITY = 16;
175 
176         /**
177          * The table, resized as necessary.
178          * table.length MUST always be a power of two.
179          */
180         private Entry[] table;
181 
182         /**
183          * The number of entries in the table.
184          */
185         private int size = 0;
186 
187         /**
188          * The next size value at which to resize.
189          */
190         private int threshold; // Default to 0
191 
192         /**
193          * Set the resize threshold to maintain at worst a 2/3 load factor.
194          */
195         private void setThreshold(int len) {
196             threshold = len * 2 / 3;
197         }
198 
199         /**
200          * Increment i modulo len.
201          */
202         private static int nextIndex(int i, int len) {
203             return ((i + 1 < len) ? i + 1 : 0);
204         }
205 
206         /**
207          * Decrement i modulo len.
208          */
209         private static int prevIndex(int i, int len) {
210             return ((i - 1 >= 0) ? i - 1 : len - 1);
211         }
212 
213         /**
214          * Construct a new map initially containing (firstKey, firstValue).
215          * ThreadLocalMaps are constructed lazily, so we only create
216          * one when we have at least one entry to put in it.
217          */
218         ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
219             table = new Entry[INITIAL_CAPACITY];
220             int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
221             table[i] = new Entry(firstKey, firstValue);
222             size = 1;
223             setThreshold(INITIAL_CAPACITY);
224         }
225 
226         /**
227          * Construct a new map including all Inheritable ThreadLocals
228          * from given parent map. Called only by createInheritedMap.
229          *
230          * @param parentMap the map associated with parent thread.
231          */
232         private ThreadLocalMap(ThreadLocalMap parentMap) {
233             Entry[] parentTable = parentMap.table;
234             int len = parentTable.length;
235             setThreshold(len);
236             table = new Entry[len];
237 
238             for (int j = 0; j < len; j++) {
239                 Entry e = parentTable[j];
240                 if (e != null) {
241                     @SuppressWarnings("unchecked")
242                     ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
243                     if (key != null) {
244                         Object value = key.childValue(e.value);
245                         Entry c = new Entry(key, value);
246                         int h = key.threadLocalHashCode & (len - 1);
247                         while (table[h] != null)
248                             h = nextIndex(h, len);
249                         table[h] = c;
250                         size++;
251                     }
252                 }
253             }
254         }
255 
256         /**
257          * Get the entry associated with key.  This method
258          * itself handles only the fast path: a direct hit of existing
259          * key. It otherwise relays to getEntryAfterMiss.  This is
260          * designed to maximize performance for direct hits, in part
261          * by making this method readily inlinable.
262          *
263          * @param  key the thread local object
264          * @return the entry associated with key, or null if no such
265          */
266         private Entry getEntry(ThreadLocal<?> key) {
267             int i = key.threadLocalHashCode & (table.length - 1);
268             Entry e = table[i];
269             if (e != null && e.get() == key)
270                 return e;
271             else
272                 return getEntryAfterMiss(key, i, e);
273         }
274 
275         /**
276          * Version of getEntry method for use when key is not found in
277          * its direct hash slot.
278          *
279          * @param  key the thread local object
280          * @param  i the table index for key‘s hash code
281          * @param  e the entry at table[i]
282          * @return the entry associated with key, or null if no such
283          */
284         private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
285             Entry[] tab = table;
286             int len = tab.length;
287 
288             while (e != null) {
289                 ThreadLocal<?> k = e.get();
290                 if (k == key)
291                     return e;
292                 if (k == null)
293                     expungeStaleEntry(i);
294                 else
295                     i = nextIndex(i, len);
296                 e = tab[i];
297             }
298             return null;
299         }
300 
301         /**
302          * Set the value associated with key.
303          *
304          * @param key the thread local object
305          * @param value the value to be set
306          */
307         private void set(ThreadLocal<?> key, Object value) {
308 
309             // We don‘t use a fast path as with get() because it is at
310             // least as common to use set() to create new entries as
311             // it is to replace existing ones, in which case, a fast
312             // path would fail more often than not.
313 
314             Entry[] tab = table;
315             int len = tab.length;
316             int i = key.threadLocalHashCode & (len-1);
317 
318             for (Entry e = tab[i];
319                  e != null;
320                  e = tab[i = nextIndex(i, len)]) {
321                 ThreadLocal<?> k = e.get();
322 
323                 if (k == key) {
324                     e.value = value;
325                     return;
326                 }
327 
328                 if (k == null) {
329                     replaceStaleEntry(key, value, i);
330                     return;
331                 }
332             }
333 
334             tab[i] = new Entry(key, value);
335             int sz = ++size;
336             if (!cleanSomeSlots(i, sz) && sz >= threshold)
337                 rehash();
338         }
339 
340         /**
341          * Remove the entry for key.
342          */
343         private void remove(ThreadLocal<?> key) {
344             Entry[] tab = table;
345             int len = tab.length;
346             int i = key.threadLocalHashCode & (len-1);
347             for (Entry e = tab[i];
348                  e != null;
349                  e = tab[i = nextIndex(i, len)]) {
350                 if (e.get() == key) {
351                     e.clear();
352                     expungeStaleEntry(i);
353                     return;
354                 }
355             }
356         }
357 
358         /**
359          * Replace a stale entry encountered during a set operation
360          * with an entry for the specified key.  The value passed in
361          * the value parameter is stored in the entry, whether or not
362          * an entry already exists for the specified key.
363          *
364          * As a side effect, this method expunges all stale entries in the
365          * "run" containing the stale entry.  (A run is a sequence of entries
366          * between two null slots.)
367          *
368          * @param  key the key
369          * @param  value the value to be associated with key
370          * @param  staleSlot index of the first stale entry encountered while
371          *         searching for key.
372          */
373         private void replaceStaleEntry(ThreadLocal<?> key, Object value,
374                                        int staleSlot) {
375             Entry[] tab = table;
376             int len = tab.length;
377             Entry e;
378 
379             // Back up to check for prior stale entry in current run.
380             // We clean out whole runs at a time to avoid continual
381             // incremental rehashing due to garbage collector freeing
382             // up refs in bunches (i.e., whenever the collector runs).
383             int slotToExpunge = staleSlot;
384             for (int i = prevIndex(staleSlot, len);
385                  (e = tab[i]) != null;
386                  i = prevIndex(i, len))
387                 if (e.get() == null)
388                     slotToExpunge = i;
389 
390             // Find either the key or trailing null slot of run, whichever
391             // occurs first
392             for (int i = nextIndex(staleSlot, len);
393                  (e = tab[i]) != null;
394                  i = nextIndex(i, len)) {
395                 ThreadLocal<?> k = e.get();
396 
397                 // If we find key, then we need to swap it
398                 // with the stale entry to maintain hash table order.
399                 // The newly stale slot, or any other stale slot
400                 // encountered above it, can then be sent to expungeStaleEntry
401                 // to remove or rehash all of the other entries in run.
402                 if (k == key) {
403                     e.value = value;
404 
405                     tab[i] = tab[staleSlot];
406                     tab[staleSlot] = e;
407 
408                     // Start expunge at preceding stale entry if it exists
409                     if (slotToExpunge == staleSlot)
410                         slotToExpunge = i;
411                     cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
412                     return;
413                 }
414 
415                 // If we didn‘t find stale entry on backward scan, the
416                 // first stale entry seen while scanning for key is the
417                 // first still present in the run.
418                 if (k == null && slotToExpunge == staleSlot)
419                     slotToExpunge = i;
420             }
421 
422             // If key not found, put new entry in stale slot
423             tab[staleSlot].value = null;
424             tab[staleSlot] = new Entry(key, value);
425 
426             // If there are any other stale entries in run, expunge them
427             if (slotToExpunge != staleSlot)
428                 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
429         }
430 
431         /**
432          * Expunge a stale entry by rehashing any possibly colliding entries
433          * lying between staleSlot and the next null slot.  This also expunges
434          * any other stale entries encountered before the trailing null.  See
435          * Knuth, Section 6.4
436          *
437          * @param staleSlot index of slot known to have null key
438          * @return the index of the next null slot after staleSlot
439          * (all between staleSlot and this slot will have been checked
440          * for expunging).
441          */
442         private int expungeStaleEntry(int staleSlot) {
443             Entry[] tab = table;
444             int len = tab.length;
445 
446             // expunge entry at staleSlot
447             tab[staleSlot].value = null;
448             tab[staleSlot] = null;
449             size--;
450 
451             // Rehash until we encounter null
452             Entry e;
453             int i;
454             for (i = nextIndex(staleSlot, len);
455                  (e = tab[i]) != null;
456                  i = nextIndex(i, len)) {
457                 ThreadLocal<?> k = e.get();
458                 if (k == null) {
459                     e.value = null;
460                     tab[i] = null;
461                     size--;
462                 } else {
463                     int h = k.threadLocalHashCode & (len - 1);
464                     if (h != i) {
465                         tab[i] = null;
466 
467                         // Unlike Knuth 6.4 Algorithm R, we must scan until
468                         // null because multiple entries could have been stale.
469                         while (tab[h] != null)
470                             h = nextIndex(h, len);
471                         tab[h] = e;
472                     }
473                 }
474             }
475             return i;
476         }
477 
478         /**
479          * Heuristically scan some cells looking for stale entries.
480          * This is invoked when either a new element is added, or
481          * another stale one has been expunged. It performs a
482          * logarithmic number of scans, as a balance between no
483          * scanning (fast but retains garbage) and a number of scans
484          * proportional to number of elements, that would find all
485          * garbage but would cause some insertions to take O(n) time.
486          *
487          * @param i a position known NOT to hold a stale entry. The
488          * scan starts at the element after i.
489          *
490          * @param n scan control: {@code log2(n)} cells are scanned,
491          * unless a stale entry is found, in which case
492          * {@code log2(table.length)-1} additional cells are scanned.
493          * When called from insertions, this parameter is the number
494          * of elements, but when from replaceStaleEntry, it is the
495          * table length. (Note: all this could be changed to be either
496          * more or less aggressive by weighting n instead of just
497          * using straight log n. But this version is simple, fast, and
498          * seems to work well.)
499          *
500          * @return true if any stale entries have been removed.
501          */
502         private boolean cleanSomeSlots(int i, int n) {
503             boolean removed = false;
504             Entry[] tab = table;
505             int len = tab.length;
506             do {
507                 i = nextIndex(i, len);
508                 Entry e = tab[i];
509                 if (e != null && e.get() == null) {
510                     n = len;
511                     removed = true;
512                     i = expungeStaleEntry(i);
513                 }
514             } while ( (n >>>= 1) != 0);
515             return removed;
516         }
517 
518         /**
519          * Re-pack and/or re-size the table. First scan the entire
520          * table removing stale entries. If this doesn‘t sufficiently
521          * shrink the size of the table, double the table size.
522          */
523         private void rehash() {
524             expungeStaleEntries();
525 
526             // Use lower threshold for doubling to avoid hysteresis
527             if (size >= threshold - threshold / 4)
528                 resize();
529         }
530 
531         /**
532          * Double the capacity of the table.
533          */
534         private void resize() {
535             Entry[] oldTab = table;
536             int oldLen = oldTab.length;
537             int newLen = oldLen * 2;
538             Entry[] newTab = new Entry[newLen];
539             int count = 0;
540 
541             for (int j = 0; j < oldLen; ++j) {
542                 Entry e = oldTab[j];
543                 if (e != null) {
544                     ThreadLocal<?> k = e.get();
545                     if (k == null) {
546                         e.value = null; // Help the GC
547                     } else {
548                         int h = k.threadLocalHashCode & (newLen - 1);
549                         while (newTab[h] != null)
550                             h = nextIndex(h, newLen);
551                         newTab[h] = e;
552                         count++;
553                     }
554                 }
555             }
556 
557             setThreshold(newLen);
558             size = count;
559             table = newTab;
560         }
561 
562         /**
563          * Expunge all stale entries in the table.
564          */
565         private void expungeStaleEntries() {
566             Entry[] tab = table;
567             int len = tab.length;
568             for (int j = 0; j < len; j++) {
569                 Entry e = tab[j];
570                 if (e != null && e.get() == null)
571                     expungeStaleEntry(j);
572             }
573         }
574     }
575 }
View Code

 

 

ThreadLocal

标签:

原文地址:http://www.cnblogs.com/luangeng/p/5785527.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!