线程系列3---ThreadLocal类研究_移动开发_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > 移动开发 > 线程系列3---ThreadLocal类研究

线程系列3---ThreadLocal类研究

 2013/12/23 19:09:08  wlrhnh  博客园  我要评论(0)
  • 摘要:2013-12-2317:44:44Java为线程安全提供了一些工具类,如ThreadLocal类,它代表一个线程局部变量,通过把数据放在ThreadLocal中就可以让每个线程创建一个该变量的副本,从而避免并发访问的线程安全问题。线程局部变量的功能其实很简单,就是为每一个使用该变量的线程提供一个副本,使每一个线程都可以独立的访问属于自己的副本,而不会和其他线程的副本产生冲突,就好像每一个线程都完全拥有该变量一样。ThreadLocal类并不能替代同步机制,两者面向的问题领域不同
  • 标签:thread 研究 线程

2013-12-23 17:44:44

Java为线程安全提供了一些工具类,如ThreadLocal类,它代表一个线程局部变量,通过把数据放在ThreadLocal中就可以让每个线程创建一个该变量的副本,从而避免并发访问的线程安全问题。

线程局部变量的功能其实很简单,就是为每一个使用该变量的线程提供一个副本,使每一个线程都可以独立的访问属于自己的副本,而不会和其他线程的副本产生冲突,就好像每一个线程都完全拥有该变量一样。

ThreadLocal类并不能替代同步机制,两者面向的问题领域不同。

同步机制是为了同步多个线程对相同资源的并发访问,是多个线程之间进行通信的有效方式;

ThreadLocal是为了隔离多个线程的数据共享,从根本上避免多个线程对共享资源的竞争,也就不需要对多个线程进行同步了。

自己做了一个Demo, 如下:

ThreadLocalTest.java

 1 package thread;
 2 
 3 public class ThreadLocalTest {
 4 
 5     public ThreadLocal<String> local = new ThreadLocal<String>();
 6 
 7     private String name;
 8 
 9     public ThreadLocalTest() {
10     }
11 
12     public String getName() {
13         // return name;
14         return local.get();
15     }
16 
17     public void setName(String name) {
18         // this.name = name;
19         local.set(name);
20     }
21 
22     public static void main(String[] args) {
23         ThreadLocalTest one = new ThreadLocalTest();
24         ThreadOne t1 = new ThreadOne(one);
25         ThreadTwo t2 = new ThreadTwo(one);
26         t1.start();
27         t2.start();
28     }
29 }

ThreadOne.java

 1 package thread;
 2 
 3 public class ThreadOne extends Thread {
 4     ThreadLocalTest one;
 5 
 6     public ThreadOne(ThreadLocalTest o) {
 7         one = o;
 8     }
 9 
10     @Override
11     public void run() {
12         for (int i = 0; i < 10; i++) {
13             StringBuilder ss = new StringBuilder("      one_");
14             ss.append(i);
15             one.setName(ss.toString());
16             try {
17                 Thread.sleep(280);
18             } catch (InterruptedException e) {
19                 e.printStackTrace();
20             }
21             System.out.println("线程一:" + Thread.currentThread().getName()
22                     + one.getName());
23         }
24     }
25 }

ThreadTwo.java

 1 package thread;
 2 
 3 public class ThreadTwo extends Thread {
 4     ThreadLocalTest one;
 5 
 6     public ThreadTwo(ThreadLocalTest o) {
 7         one = o;
 8     }
 9 
10     @Override
11     public void run() {
12         for (int i = 0; i < 10; i++) {
13             StringBuilder ss = new StringBuilder("------------two_");
14             ss.append(i);
15             one.setName(ss.toString());
16             try {
17                 Thread.sleep(100);
18             } catch (InterruptedException e) {
19                 e.printStackTrace();
20             }
21             System.out.println("线程二:" + Thread.currentThread().getName()
22                     + one.getName());
23         }
24     }
25 }

先看不是用ThreadLocal的log(使用ThreadLocalTest.java中注释掉的部分),如下:

 1 线程二:Thread-1------------two_0
 2 线程二:Thread-1------------two_1
 3 线程一:Thread-0------------two_2
 4 线程二:Thread-1      one_1
 5 线程二:Thread-1------------two_3
 6 线程二:Thread-1------------two_4
 7 线程一:Thread-0------------two_5
 8 线程二:Thread-1      one_2
 9 线程二:Thread-1------------two_6
10 线程二:Thread-1------------two_7
11 线程一:Thread-0------------two_8
12 线程二:Thread-1      one_3
13 线程二:Thread-1------------two_9
14 线程一:Thread-0------------two_9
15 线程一:Thread-0      one_4
16 线程一:Thread-0      one_5
17 线程一:Thread-0      one_6
18 线程一:Thread-0      one_7
19 线程一:Thread-0      one_8
20 线程一:Thread-0      one_9

可以上面红色部分,可以发现就是多线程导致资源混乱的情况。

下面是使用了ThreadLocal后的log:

 1 线程二:Thread-1------------two_0
 2 线程二:Thread-1------------two_1
 3 线程一:Thread-0      one_0
 4 线程二:Thread-1------------two_2
 5 线程二:Thread-1------------two_3
 6 线程二:Thread-1------------two_4
 7 线程一:Thread-0      one_1
 8 线程二:Thread-1------------two_5
 9 线程二:Thread-1------------two_6
10 线程二:Thread-1------------two_7
11 线程一:Thread-0      one_2
12 线程二:Thread-1------------two_8
13 线程二:Thread-1------------two_9
14 线程一:Thread-0      one_3
15 线程一:Thread-0      one_4
16 线程一:Thread-0      one_5
17 线程一:Thread-0      one_6
18 线程一:Thread-0      one_7
19 线程一:Thread-0      one_8
20 线程一:Thread-0      one_9

 一切正常。

但是我发先了一个问题,那就是ThreadLocal类在Android和Java中的实现不太一样,把它们的源码都贴出来,有兴趣的同学可以好好研究一下:

Java SE-1.6

logs_code_hide('f30dc5fe-3e96-423b-ab17-97ea35bb08df',event)" src="/Upload/Images/2013122319/2B1B950FA3DF188F.gif" alt="" />
  1 /*
  2  * %W% %E%
  3  *
  4  * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
  5  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6  */
  7 
  8 package java.lang;
  9 import java.lang.ref.*;
 10 import java.util.concurrent.atomic.AtomicInteger;
 11 
 12 /**
 13  * This class provides thread-local variables.  These variables differ from
 14  * their normal counterparts in that each thread that accesses one (via its
 15  * <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized
 16  * copy of the variable.  <tt>ThreadLocal</tt> instances are typically private
 17  * static fields in classes that wish to associate state with a thread (e.g.,
 18  * a user ID or Transaction ID).
 19  *
 20  * <p>For example, the class below generates unique identifiers local to each
 21  * thread.
 22  * A thread's id is
 23  * assigned the first time it invokes <tt>UniqueThreadIdGenerator.getCurrentThreadId()</tt> and remains unchanged on subsequent calls.
 24  * <pre>
 25  * import java.util.concurrent.atomic.AtomicInteger;
 26  *
 27  * public class UniqueThreadIdGenerator {
 28  *
 29  *     private static final AtomicInteger uniqueId = new AtomicInteger(0);
 30  *
 31  *     private static final ThreadLocal &lt; Integer > uniqueNum = 
 32  *         new ThreadLocal &lt; Integer > () {
 33  *             &#64;Override protected Integer initialValue() {
 34  *                 return uniqueId.getAndIncrement();
 35  *         }
 36  *     };
 37  * 
 38  *     public static int getCurrentThreadId() {
 39  *         return uniqueId.get();
 40  *     }
 41  * } // UniqueThreadIdGenerator
 42  * </pre>
 43  * <p>Each thread holds an implicit reference to its copy of a thread-local
 44  * variable as long as the thread is alive and the <tt>ThreadLocal</tt>
 45  * instance is accessible; after a thread goes away, all of its copies of
 46  * thread-local instances are subject to garbage collection (unless other
 47  * references to these copies exist). 
 48  *
 49  * @author  Josh Bloch and Doug Lea
 50  * @version %I%, %G%
 51  * @since   1.2
 52  */
 53 public class ThreadLocal<T> {
 54     /**
 55      * ThreadLocals rely on per-thread linear-probe hash maps attached
 56      * to each thread (Thread.threadLocals and
 57      * inheritableThreadLocals).  The ThreadLocal objects act as keys,
 58      * searched via threadLocalHashCode.  This is a custom hash code
 59      * (useful only within ThreadLocalMaps) that eliminates collisions
 60      * in the common case where consecutively constructed ThreadLocals
 61      * are used by the same threads, while remaining well-behaved in
 62      * less common cases.
 63      */
 64     private final int threadLocalHashCode = nextHashCode();
 65 
 66     /**
 67      * The next hash code to be given out. Updated atomically. Starts at
 68      * zero.
 69      */
 70     private static AtomicInteger nextHashCode = 
 71     new AtomicInteger();
 72 
 73     /**
 74      * The difference between successively generated hash codes - turns
 75      * implicit sequential thread-local IDs into near-optimally spread
 76      * multiplicative hash values for power-of-two-sized tables.
 77      */
 78     private static final int HASH_INCREMENT = 0x61c88647;
 79 
 80     /**
 81      * Returns the next hash code.
 82      */
 83     private static int nextHashCode() {
 84     return nextHashCode.getAndAdd(HASH_INCREMENT); 
 85     }
 86 
 87     /**
 88      * Returns the current thread's "initial value" for this
 89      * thread-local variable.  This method will be invoked the first
 90      * time a thread accesses the variable with the {@link #get}
 91      * method, unless the thread previously invoked the {@link #set}
 92      * method, in which case the <tt>initialValue</tt> method will not
 93      * be invoked for the thread.  Normally, this method is invoked at
 94      * most once per thread, but it may be invoked again in case of
 95      * subsequent invocations of {@link #remove} followed by {@link #get}.
 96      *
 97      * <p>This implementation simply returns <tt>null</tt>; if the
 98      * programmer desires thread-local variables to have an initial
 99      * value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be
100      * subclassed, and this method overridden.  Typically, an
101      * anonymous inner class will be used.
102      *
103      * @return the initial value for this thread-local
104      */
105     protected T initialValue() {
106         return null;
107     }
108 
109     /**
110      * Creates a thread local variable.
111      */
112     public ThreadLocal() {
113     }
114 
115     /**
116      * Returns the value in the current thread's copy of this
117      * thread-local variable.  If the variable has no value for the
118      * current thread, it is first initialized to the value returned
119      * by an invocation of the {@link #initialValue} method.
120      *
121      * @return the current thread's value of this thread-local
122      */
123     public T get() {
124         Thread t = Thread.currentThread();
125         ThreadLocalMap map = getMap(t);
126         if (map != null) {
127             ThreadLocalMap.Entry e = map.getEntry(this);
128             if (e != null)
129                 return (T)e.value;
130         }
131         return setInitialValue();
132     }
133 
134     /**
135      * Variant of set() to establish initialValue. Used instead
136      * of set() in case user has overridden the set() method.
137      *
138      * @return the initial value
139      */
140     private T setInitialValue() {
141         T value = initialValue();
142         Thread t = Thread.currentThread();
143         ThreadLocalMap map = getMap(t);
144         if (map != null)
145             map.set(this, value);
146         else
147             createMap(t, value);
148         return value;
149     }
150 
151     /**
152      * Sets the current thread's copy of this thread-local variable
153      * to the specified value.  Most subclasses will have no need to 
154      * override this method, relying solely on the {@link #initialValue}
155      * method to set the values of thread-locals.
156      *
157      * @param value the value to be stored in the current thread's copy of
158      *        this thread-local.
159      */
160     public void set(T value) {
161         Thread t = Thread.currentThread();
162         ThreadLocalMap map = getMap(t);
163         if (map != null)
164             map.set(this, value);
165         else
166             createMap(t, value);
167     }
168 
169     /**
170      * Removes the current thread's value for this thread-local
171      * variable.  If this thread-local variable is subsequently
172      * {@linkplain #get read} by the current thread, its value will be
173      * reinitialized by invoking its {@link #initialValue} method,
174      * unless its value is {@linkplain #set set} by the current thread
175      * in the interim.  This may result in multiple invocations of the
176      * <tt>initialValue</tt> method in the current thread.
177      *
178      * @since 1.5
179      */
180      public void remove() {
181          ThreadLocalMap m = getMap(Thread.currentThread());
182          if (m != null)
183              m.remove(this);
184      }
185 
186     /**
187      * Get the map associated with a ThreadLocal. Overridden in
188      * InheritableThreadLocal.
189      *
190      * @param  t the current thread
191      * @return the map
192      */
193     ThreadLocalMap getMap(Thread t) {
194         return t.threadLocals;
195     }
196 
197     /**
198      * Create the map associated with a ThreadLocal. Overridden in
199      * InheritableThreadLocal.
200      *
201      * @param t the current thread
202      * @param firstValue value for the initial entry of the map
203      * @param map the map to store.
204      */
205     void createMap(Thread t, T firstValue) {
206         t.threadLocals = new ThreadLocalMap(this, firstValue);
207     }
208 
209     /**
210      * Factory method to create map of inherited thread locals.
211      * Designed to be called only from Thread constructor.
212      *
213      * @param  parentMap the map associated with parent thread
214      * @return a map containing the parent's inheritable bindings
215      */
216     static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
217         return new ThreadLocalMap(parentMap);
218     }
219 
220     /**
221      * Method childValue is visibly defined in subclass
222      * InheritableThreadLocal, but is internally defined here for the
223      * sake of providing createInheritedMap factory method without
224      * needing to subclass the map class in InheritableThreadLocal.
225      * This technique is preferable to the alternative of embedding
226      * instanceof tests in methods.
227      */
228     T childValue(T parentValue) {
229         throw new UnsupportedOperationException();
230     }
231 
232     /**
233      * ThreadLocalMap is a customized hash map suitable only for
234      * maintaining thread local values. No operations are exported
235      * outside of the ThreadLocal class. The class is package private to
236      * allow declaration of fields in class Thread.  To help deal with
237      * very large and long-lived usages, the hash table entries use
238      * WeakReferences for keys. However, since reference queues are not
239      * used, stale entries are guaranteed to be removed only when
240      * the table starts running out of space.
241      */
242     static class ThreadLocalMap {
243 
244         /**
245          * The entries in this hash map extend WeakReference, using
246          * its main ref field as the key (which is always a
247          * ThreadLocal object).  Note that null keys (i.e. entry.get()
248          * == null) mean that the key is no longer referenced, so the
249          * entry can be expunged from table.  Such entries are referred to
250          * as "stale entries" in the code that follows.
251          */
252         static class Entry extends WeakReference<ThreadLocal> {
253             /** The value associated with this ThreadLocal. */
254             Object value;
255 
256             Entry(ThreadLocal k, Object v) {
257                 super(k);
258                 value = v;
259             }
260         }
261 
262         /**
263          * The initial capacity -- MUST be a power of two.
264          */
265         private static final int INITIAL_CAPACITY = 16;
266 
267         /**
268          * The table, resized as necessary.
269          * table.length MUST always be a power of two.
270          */
271         private Entry[] table;
272 
273         /**
274          * The number of entries in the table.
275          */
276         private int size = 0;
277 
278         /**
279          * The next size value at which to resize.
280          */
281         private int threshold; // Default to 0
282 
283         /**
284          * Set the resize threshold to maintain at worst a 2/3 load factor.
285          */
286         private void setThreshold(int len) {
287             threshold = len * 2 / 3;
288         }
289 
290         /**
291          * Increment i modulo len.
292          */
293         private static int nextIndex(int i, int len) {
294             return ((i + 1 < len) ? i + 1 : 0);
295         }
296 
297         /**
298          * Decrement i modulo len.
299          */
300         private static int prevIndex(int i, int len) {
301             return ((i - 1 >= 0) ? i - 1 : len - 1);
302         }
303 
304         /**
305          * Construct a new map initially containing (firstKey, firstValue).
306          * ThreadLocalMaps are constructed lazily, so we only create
307          * one when we have at least one entry to put in it.
308          */
309         ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
310             table = new Entry[INITIAL_CAPACITY];
311             int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
312             table[i] = new Entry(firstKey, firstValue);
313             size = 1;
314             setThreshold(INITIAL_CAPACITY);
315         }
316 
317         /**
318          * Construct a new map including all Inheritable ThreadLocals
319          * from given parent map. Called only by createInheritedMap.
320          *
321          * @param parentMap the map associated with parent thread.
322          */
323         private ThreadLocalMap(ThreadLocalMap parentMap) {
324             Entry[] parentTable = parentMap.table;
325             int len = parentTable.length;
326             setThreshold(len);
327             table = new Entry[len];
328 
329             for (int j = 0; j < len; j++) {
330                 Entry e = parentTable[j];
331                 if (e != null) {
332                     ThreadLocal key = e.get();
333                     if (key != null) {
334                         Object value = key.childValue(e.value);
335                         Entry c = new Entry(key, value);
336                         int h = key.threadLocalHashCode & (len - 1);
337                         while (table[h] != null)
338                             h = nextIndex(h, len);
339                         table[h] = c;
340                         size++;
341                     }
342                 }
343             }
344         }
345 
346         /**
347          * Get the entry associated with key.  This method
348          * itself handles only the fast path: a direct hit of existing
349          * key. It otherwise relays to getEntryAfterMiss.  This is
350          * designed to maximize performance for direct hits, in part
351          * by making this method readily inlinable.
352          *
353          * @param  key the thread local object
354          * @return the entry associated with key, or null if no such
355          */
356         private Entry getEntry(ThreadLocal key) {
357             int i = key.threadLocalHashCode & (table.length - 1);
358             Entry e = table[i];
359             if (e != null && e.get() == key)
360                 return e;
361             else
362                 return getEntryAfterMiss(key, i, e);
363         }
364 
365         /**
366          * Version of getEntry method for use when key is not found in
367          * its direct hash slot.
368          *
369          * @param  key the thread local object
370          * @param  i the table index for key's hash code
371          * @param  e the entry at table[i]
372          * @return the entry associated with key, or null if no such
373          */
374         private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
375             Entry[] tab = table;
376             int len = tab.length;
377 
378             while (e != null) {
379                 ThreadLocal k = e.get();
380                 if (k == key)
381                     return e;
382                 if (k == null)
383                     expungeStaleEntry(i);
384                 else
385                     i = nextIndex(i, len);
386                 e = tab[i];
387             }
388             return null;
389         }
390 
391         /**
392          * Set the value associated with key.
393          *
394          * @param key the thread local object
395          * @param value the value to be set
396          */
397         private void set(ThreadLocal key, Object value) {
398 
399             // We don't use a fast path as with get() because it is at
400             // least as common to use set() to create new entries as
401             // it is to replace existing ones, in which case, a fast
402             // path would fail more often than not.
403 
404             Entry[] tab = table;
405             int len = tab.length;
406             int i = key.threadLocalHashCode & (len-1);
407 
408             for (Entry e = tab[i];
409          e != null;
410          e = tab[i = nextIndex(i, len)]) {
411                 ThreadLocal k = e.get();
412 
413                 if (k == key) {
414                     e.value = value;
415                     return;
416                 }
417 
418                 if (k == null) {
419                     replaceStaleEntry(key, value, i);
420                     return;
421                 }
422             }
423 
424             tab[i] = new Entry(key, value);
425             int sz = ++size;
426             if (!cleanSomeSlots(i, sz) && sz >= threshold)
427                 rehash();
428         }
429 
430         /**
431          * Remove the entry for key.
432          */
433         private void remove(ThreadLocal key) {
434             Entry[] tab = table;
435             int len = tab.length;
436             int i = key.threadLocalHashCode & (len-1);
437             for (Entry e = tab[i];
438          e != null;
439          e = tab[i = nextIndex(i, len)]) {
440                 if (e.get() == key) {
441                     e.clear();
442                     expungeStaleEntry(i);
443                     return;
444                 }
445             }
446         }
447 
448         /**
449          * Replace a stale entry encountered during a set operation
450          * with an entry for the specified key.  The value passed in
451          * the value parameter is stored in the entry, whether or not
452          * an entry already exists for the specified key.
453          *
454          * As a side effect, this method expunges all stale entries in the
455          * "run" containing the stale entry.  (A run is a sequence of entries
456          * between two null slots.)
457          *
458          * @param  key the key
459          * @param  value the value to be associated with key
460          * @param  staleSlot index of the first stale entry encountered while
461          *         searching for key.
462          */
463         private void replaceStaleEntry(ThreadLocal key, Object value,
464                                        int staleSlot) {
465             Entry[] tab = table;
466             int len = tab.length;
467             Entry e;
468 
469             // Back up to check for prior stale entry in current run.
470             // We clean out whole runs at a time to avoid continual
471             // incremental rehashing due to garbage collector freeing
472             // up refs in bunches (i.e., whenever the collector runs).
473             int slotToExpunge = staleSlot;
474             for (int i = prevIndex(staleSlot, len);
475          (e = tab[i]) != null;
476                  i = prevIndex(i, len))
477                 if (e.get() == null)
478                     slotToExpunge = i;
479 
480             // Find either the key or trailing null slot of run, whichever
481             // occurs first
482             for (int i = nextIndex(staleSlot, len);
483          (e = tab[i]) != null;
484                  i = nextIndex(i, len)) {
485                 ThreadLocal k = e.get();
486 
487                 // If we find key, then we need to swap it
488                 // with the stale entry to maintain hash table order.
489                 // The newly stale slot, or any other stale slot
490                 // encountered above it, can then be sent to expungeStaleEntry
491                 // to remove or rehash all of the other entries in run.
492                 if (k == key) {
493                     e.value = value;
494 
495                     tab[i] = tab[staleSlot];
496                     tab[staleSlot] = e;
497 
498                     // Start expunge at preceding stale entry if it exists
499                     if (slotToExpunge == staleSlot)
500                         slotToExpunge = i;
501                     cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
502                     return;
503                 }
504 
505                 // If we didn't find stale entry on backward scan, the
506                 // first stale entry seen while scanning for key is the
507                 // first still present in the run.
508                 if (k == null && slotToExpunge == staleSlot)
509                     slotToExpunge = i;
510             }
511 
512             // If key not found, put new entry in stale slot
513             tab[staleSlot].value = null;   
514             tab[staleSlot] = new Entry(key, value);
515 
516             // If there are any other stale entries in run, expunge them
517             if (slotToExpunge != staleSlot)
518                 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
519         }
520 
521         /**
522          * Expunge a stale entry by rehashing any possibly colliding entries
523          * lying between staleSlot and the next null slot.  This also expunges
524          * any other stale entries encountered before the trailing null.  See
525          * Knuth, Section 6.4
526          *
527          * @param staleSlot index of slot known to have null key
528          * @return the index of the next null slot after staleSlot
529          * (all between staleSlot and this slot will have been checked
530          * for expunging).
531          */
532         private int expungeStaleEntry(int staleSlot) {
533             Entry[] tab = table;
534             int len = tab.length;
535 
536             // expunge entry at staleSlot
537             tab[staleSlot].value = null;   
538             tab[staleSlot] = null;
539             size--;
540 
541             // Rehash until we encounter null
542             Entry e;
543             int i;
544             for (i = nextIndex(staleSlot, len);
545          (e = tab[i]) != null;
546                  i = nextIndex(i, len)) {
547                 ThreadLocal k = e.get();
548                 if (k == null) {
549                     e.value = null;
550                     tab[i] = null;
551                     size--;
552                 } else {
553                     int h = k.threadLocalHashCode & (len - 1);
554                     if (h != i) {
555                         tab[i] = null;
556 
557                         // Unlike Knuth 6.4 Algorithm R, we must scan until
558                         // null because multiple entries could have been stale.
559                         while (tab[h] != null)
560                             h = nextIndex(h, len);
561                         tab[h] = e;
562                     }
563                 }
564             }
565             return i;
566         }
567 
568         /**
569          * Heuristically scan some cells looking for stale entries.
570          * This is invoked when either a new element is added, or
571          * another stale one has been expunged. It performs a
572          * logarithmic number of scans, as a balance between no
573          * scanning (fast but retains garbage) and a number of scans
574          * proportional to number of elements, that would find all
575          * garbage but would cause some insertions to take O(n) time.
576          *
577          * @param i a position known NOT to hold a stale entry. The
578          * scan starts at the element after i.
579          *
580          * @param n scan control: <tt>log2(n)</tt> cells are scanned,
581          * unless a stale entry is found, in which case
582          * <tt>log2(table.length)-1</tt> additional cells are scanned.
583          * When called from insertions, this parameter is the number
584          * of elements, but when from replaceStaleEntry, it is the
585          * table length. (Note: all this could be changed to be either
586          * more or less aggressive by weighting n instead of just
587          * using straight log n. But this version is simple, fast, and
588          * seems to work well.)
589          *
590          * @return true if any stale entries have been removed.
591          */
592         private boolean cleanSomeSlots(int i, int n) {
593             boolean removed = false;
594             Entry[] tab = table;
595             int len = tab.length;
596             do {
597                 i = nextIndex(i, len);
598                 Entry e = tab[i];
599                 if (e != null && e.get() == null) {
600                     n = len;
601                     removed = true;
602                     i = expungeStaleEntry(i);
603                 }
604             } while ( (n >>>= 1) != 0);
605             return removed;
606         }
607 
608         /**
609          * Re-pack and/or re-size the table. First scan the entire
610          * table removing stale entries. If this doesn't sufficiently
611          * shrink the size of the table, double the table size.
612          */
613         private void rehash() {
614             expungeStaleEntries();
615 
616             // Use lower threshold for doubling to avoid hysteresis
617             if (size >= threshold - threshold / 4)
618                 resize();
619         }
620 
621         /**
622          * Double the capacity of the table.
623          */
624         private void resize() {
625             Entry[] oldTab = table;
626             int oldLen = oldTab.length;
627             int newLen = oldLen * 2;
628             Entry[] newTab = new Entry[newLen];
629             int count = 0;
630 
631             for (int j = 0; j < oldLen; ++j) {
632                 Entry e = oldTab[j];
633                 if (e != null) {
634                     ThreadLocal k = e.get();
635                     if (k == null) {
636                         e.value = null; // Help the GC
637                     } else {
638                         int h = k.threadLocalHashCode & (newLen - 1);
639                         while (newTab[h] != null)
640                             h = nextIndex(h, newLen);
641                         newTab[h] = e;
642                         count++;
643                     }
644                 }
645             }
646 
647             setThreshold(newLen);
648             size = count;
649             table = newTab;
650         }
651 
652         /**
653          * Expunge all stale entries in the table.
654          */
655         private void expungeStaleEntries() {
656             Entry[] tab = table;
657             int len = tab.length;
658             for (int j = 0; j < len; j++) {
659                 Entry e = tab[j];
660                 if (e != null && e.get() == null)
661                     expungeStaleEntry(j);
662             }
663         }
664     }
665 }
View Code

Android 4.4

  1 /*
  2  * Copyright (C) 2008 The Android Open Source Project
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 package java.lang;
 18 
 19 import java.lang.ref.Reference;
 20 import java.lang.ref.WeakReference;
 21 import java.util.concurrent.atomic.AtomicInteger;
 22 
 23 /**
 24  * Implements a thread-local storage, that is, a variable for which each thread
 25  * has its own value. All threads share the same {@code ThreadLocal} object,
 26  * but each sees a different value when accessing it, and changes made by one
 27  * thread do not affect the other threads. The implementation supports
 28  * {@code null} values.
 29  *
 30  * @see java.lang.Thread
 31  * @author Bob Lee
 32  */
 33 public class ThreadLocal<T> {
 34 
 35     /* Thanks to Josh Bloch and Doug Lea for code reviews and impl advice. */
 36 
 37     /**
 38      * Creates a new thread-local variable.
 39      */
 40     public ThreadLocal() {}
 41 
 42     /**
 43      * Returns the value of this variable for the current thread. If an entry
 44      * doesn't yet exist for this variable on this thread, this method will
 45      * create an entry, populating the value with the result of
 46      * {@link #initialValue()}.
 47      *
 48      * @return the current value of the variable for the calling thread.
 49      */
 50     @SuppressWarnings("unchecked")
 51     public T get() {
 52         // Optimized for the fast path.
 53         Thread currentThread = Thread.currentThread();
 54         Values values = values(currentThread);
 55         if (values != null) {
 56             Object[] table = values.table;
 57             int index = hash & values.mask;
 58             if (this.reference == table[index]) {
 59                 return (T) table[index + 1];
 60             }
 61         } else {
 62             values = initializeValues(currentThread);
 63         }
 64 
 65         return (T) values.getAfterMiss(this);
 66     }
 67 
 68     /**
 69      * Provides the initial value of this variable for the current thread.
 70      * The default implementation returns {@code null}.
 71      *
 72      * @return the initial value of the variable.
 73      */
 74     protected T initialValue() {
 75         return null;
 76     }
 77 
 78     /**
 79      * Sets the value of this variable for the current thread. If set to
 80      * {@code null}, the value will be set to null and the underlying entry will
 81      * still be present.
 82      *
 83      * @param value the new value of the variable for the caller thread.
 84      */
 85     public void set(T value) {
 86         Thread currentThread = Thread.currentThread();
 87         Values values = values(currentThread);
 88         if (values == null) {
 89             values = initializeValues(currentThread);
 90         }
 91         values.put(this, value);
 92     }
 93 
 94     /**
 95      * Removes the entry for this variable in the current thread. If this call
 96      * is followed by a {@link #get()} before a {@link #set},
 97      * {@code #get()} will call {@link #initialValue()} and create a new
 98      * entry with the resulting value.
 99      *
100      * @since 1.5
101      */
102     public void remove() {
103         Thread currentThread = Thread.currentThread();
104         Values values = values(currentThread);
105         if (values != null) {
106             values.remove(this);
107         }
108     }
109 
110     /**
111      * Creates Values instance for this thread and variable type.
112      */
113     Values initializeValues(Thread current) {
114         return current.localValues = new Values();
115     }
116 
117     /**
118      * Gets Values instance for this thread and variable type.
119      */
120     Values values(Thread current) {
121         return current.localValues;
122     }
123 
124     /** Weak reference to this thread local instance. */
125     private final Reference<ThreadLocal<T>> reference
126             = new WeakReference<ThreadLocal<T>>(this);
127 
128     /** Hash counter. */
129     private static AtomicInteger hashCounter = new AtomicInteger(0);
130 
131     /**
132      * Internal hash. We deliberately don't bother with #hashCode().
133      * Hashes must be even. This ensures that the result of
134      * (hash & (table.length - 1)) points to a key and not a value.
135      *
136      * We increment by Doug Lea's Magic Number(TM) (*2 since keys are in
137      * every other bucket) to help prevent clustering.
138      */
139     private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);
140 
141     /**
142      * Per-thread map of ThreadLocal instances to values.
143      */
144     static class Values {
145 
146         /**
147          * Size must always be a power of 2.
148          */
149         private static final int INITIAL_SIZE = 16;
150 
151         /**
152          * Placeholder for deleted entries.
153          */
154         private static final Object TOMBSTONE = new Object();
155 
156         /**
157          * Map entries. Contains alternating keys (ThreadLocal) and values.
158          * The length is always a power of 2.
159          */
160         private Object[] table;
161 
162         /** Used to turn hashes into indices. */
163         private int mask;
164 
165         /** Number of live entries. */
166         private int size;
167 
168         /** Number of tombstones. */
169         private int tombstones;
170 
171         /** Maximum number of live entries and tombstones. */
172         private int maximumLoad;
173 
174         /** Points to the next cell to clean up. */
175         private int clean;
176 
177         /**
178          * Constructs a new, empty instance.
179          */
180         Values() {
181             initializeTable(INITIAL_SIZE);
182             this.size = 0;
183             this.tombstones = 0;
184         }
185 
186         /**
187          * Used for InheritableThreadLocals.
188          */
189         Values(Values fromParent) {
190             this.table = fromParent.table.clone();
191             this.mask = fromParent.mask;
192             this.size = fromParent.size;
193             this.tombstones = fromParent.tombstones;
194             this.maximumLoad = fromParent.maximumLoad;
195             this.clean = fromParent.clean;
196             inheritValues(fromParent);
197         }
198 
199         /**
200          * Inherits values from a parent thread.
201          */
202         @SuppressWarnings({"unchecked"})
203         private void inheritValues(Values fromParent) {
204             // Transfer values from parent to child thread.
205             Object[] table = this.table;
206             for (int i = table.length - 2; i >= 0; i -= 2) {
207                 Object k = table[i];
208 
209                 if (k == null || k == TOMBSTONE) {
210                     // Skip this entry.
211                     continue;
212                 }
213 
214                 // The table can only contain null, tombstones and references.
215                 Reference<InheritableThreadLocal<?>> reference
216                         = (Reference<InheritableThreadLocal<?>>) k;
217                 // Raw type enables us to pass in an Object below.
218                 InheritableThreadLocal key = reference.get();
219                 if (key != null) {
220                     // Replace value with filtered value.
221                     // We should just let exceptions bubble out and tank
222                     // the thread creation
223                     table[i + 1] = key.childValue(fromParent.table[i + 1]);
224                 } else {
225                     // The key was reclaimed.
226                     table[i] = TOMBSTONE;
227                     table[i + 1] = null;
228                     fromParent.table[i] = TOMBSTONE;
229                     fromParent.table[i + 1] = null;
230 
231                     tombstones++;
232                     fromParent.tombstones++;
233 
234                     size--;
235                     fromParent.size--;
236                 }
237             }
238         }
239 
240         /**
241          * Creates a new, empty table with the given capacity.
242          */
243         private void initializeTable(int capacity) {
244             this.table = new Object[capacity * 2];
245             this.mask = table.length - 1;
246             this.clean = 0;
247             this.maximumLoad = capacity * 2 / 3; // 2/3
248         }
249 
250         /**
251          * Cleans up after garbage-collected thread locals.
252          */
253         private void cleanUp() {
254             if (rehash()) {
255                 // If we rehashed, we needn't clean up (clean up happens as
256                 // a side effect).
257                 return;
258             }
259 
260             if (size == 0) {
261                 // No live entries == nothing to clean.
262                 return;
263             }
264 
265             // Clean log(table.length) entries picking up where we left off
266             // last time.
267             int index = clean;
268             Object[] table = this.table;
269             for (int counter = table.length; counter > 0; counter >>= 1,
270                     index = next(index)) {
271                 Object k = table[index];
272 
273                 if (k == TOMBSTONE || k == null) {
274                     continue; // on to next entry
275                 }
276 
277                 // The table can only contain null, tombstones and references.
278                 @SuppressWarnings("unchecked")
279                 Reference<ThreadLocal<?>> reference
280                         = (Reference<ThreadLocal<?>>) k;
281                 if (reference.get() == null) {
282                     // This thread local was reclaimed by the garbage collector.
283                     table[index] = TOMBSTONE;
284                     table[index + 1] = null;
285                     tombstones++;
286                     size--;
287                 }
288             }
289 
290             // Point cursor to next index.
291             clean = index;
292         }
293 
294         /**
295          * Rehashes the table, expanding or contracting it as necessary.
296          * Gets rid of tombstones. Returns true if a rehash occurred.
297          * We must rehash every time we fill a null slot; we depend on the
298          * presence of null slots to end searches (otherwise, we'll infinitely
299          * loop).
300          */
301         private boolean rehash() {
302             if (tombstones + size < maximumLoad) {
303                 return false;
304             }
305 
306             int capacity = table.length >> 1;
307 
308             // Default to the same capacity. This will create a table of the
309             // same size and move over the live entries, analogous to a
310             // garbage collection. This should only happen if you churn a
311             // bunch of thread local garbage (removing and reinserting
312             // the same thread locals over and over will overwrite tombstones
313             // and not fill up the table).
314             int newCapacity = capacity;
315 
316             if (size > (capacity >> 1)) {
317                 // More than 1/2 filled w/ live entries.
318                 // Double size.
319                 newCapacity = capacity * 2;
320             }
321 
322             Object[] oldTable = this.table;
323 
324             // Allocate new table.
325             initializeTable(newCapacity);
326 
327             // We won't have any tombstones after this.
328             this.tombstones = 0;
329 
330             // If we have no live entries, we can quit here.
331             if (size == 0) {
332                 return true;
333             }
334 
335             // Move over entries.
336             for (int i = oldTable.length - 2; i >= 0; i -= 2) {
337                 Object k = oldTable[i];
338                 if (k == null || k == TOMBSTONE) {
339                     // Skip this entry.
340                     continue;
341                 }
342 
343                 // The table can only contain null, tombstones and references.
344                 @SuppressWarnings("unchecked")
345                 Reference<ThreadLocal<?>> reference
346                         = (Reference<ThreadLocal<?>>) k;
347                 ThreadLocal<?> key = reference.get();
348                 if (key != null) {
349                     // Entry is still live. Move it over.
350                     add(key, oldTable[i + 1]);
351                 } else {
352                     // The key was reclaimed.
353                     size--;
354                 }
355             }
356 
357             return true;
358         }
359 
360         /**
361          * Adds an entry during rehashing. Compared to put(), this method
362          * doesn't have to clean up, check for existing entries, account for
363          * tombstones, etc.
364          */
365         void add(ThreadLocal<?> key, Object value) {
366             for (int index = key.hash & mask;; index = next(index)) {
367                 Object k = table[index];
368                 if (k == null) {
369                     table[index] = key.reference;
370                     table[index + 1] = value;
371                     return;
372                 }
373             }
374         }
375 
376         /**
377          * Sets entry for given ThreadLocal to given value, creating an
378          * entry if necessary.
379          */
380         void put(ThreadLocal<?> key, Object value) {
381             cleanUp();
382 
383             // Keep track of first tombstone. That's where we want to go back
384             // and add an entry if necessary.
385             int firstTombstone = -1;
386 
387             for (int index = key.hash & mask;; index = next(index)) {
388                 Object k = table[index];
389 
390                 if (k == key.reference) {
391                     // Replace existing entry.
392                     table[index + 1] = value;
393                     return;
394                 }
395 
396                 if (k == null) {
397                     if (firstTombstone == -1) {
398                         // Fill in null slot.
399                         table[index] = key.reference;
400                         table[index + 1] = value;
401                         size++;
402                         return;
403                     }
404 
405                     // Go back and replace first tombstone.
406                     table[firstTombstone] = key.reference;
407                     table[firstTombstone + 1] = value;
408                     tombstones--;
409                     size++;
410                     return;
411                 }
412 
413                 // Remember first tombstone.
414                 if (firstTombstone == -1 && k == TOMBSTONE) {
415                     firstTombstone = index;
416                 }
417             }
418         }
419 
420         /**
421          * Gets value for given ThreadLocal after not finding it in the first
422          * slot.
423          */
424         Object getAfterMiss(ThreadLocal<?> key) {
425             Object[] table = this.table;
426             int index = key.hash & mask;
427 
428             // If the first slot is empty, the search is over.
429             if (table[index] == null) {
430                 Object value = key.initialValue();
431 
432                 // If the table is still the same and the slot is still empty...
433                 if (this.table == table && table[index] == null) {
434                     table[index] = key.reference;
435                     table[index + 1] = value;
436                     size++;
437 
438                     cleanUp();
439                     return value;
440                 }
441 
442                 // The table changed during initialValue().
443                 put(key, value);
444                 return value;
445             }
446 
447             // Keep track of first tombstone. That's where we want to go back
448             // and add an entry if necessary.
449             int firstTombstone = -1;
450 
451             // Continue search.
452             for (index = next(index);; index = next(index)) {
453                 Object reference = table[index];
454                 if (reference == key.reference) {
455                     return table[index + 1];
456                 }
457 
458                 // If no entry was found...
459                 if (reference == null) {
460                     Object value = key.initialValue();
461 
462                     // If the table is still the same...
463                     if (this.table == table) {
464                         // If we passed a tombstone and that slot still
465                         // contains a tombstone...
466                         if (firstTombstone > -1
467                                 && table[firstTombstone] == TOMBSTONE) {
468                             table[firstTombstone] = key.reference;
469                             table[firstTombstone + 1] = value;
470                             tombstones--;
471                             size++;
472 
473                             // No need to clean up here. We aren't filling
474                             // in a null slot.
475                             return value;
476                         }
477 
478                         // If this slot is still empty...
479                         if (table[index] == null) {
480                             table[index] = key.reference;
481                             table[index + 1] = value;
482                             size++;
483 
484                             cleanUp();
485                             return value;
486                         }
487                     }
488 
489                     // The table changed during initialValue().
490                     put(key, value);
491                     return value;
492                 }
493 
494                 if (firstTombstone == -1 && reference == TOMBSTONE) {
495                     // Keep track of this tombstone so we can overwrite it.
496                     firstTombstone = index;
497                 }
498             }
499         }
500 
501         /**
502          * Removes entry for the given ThreadLocal.
503          */
504         void remove(ThreadLocal<?> key) {
505             cleanUp();
506 
507             for (int index = key.hash & mask;; index = next(index)) {
508                 Object reference = table[index];
509 
510                 if (reference == key.reference) {
511                     // Success!
512                     table[index] = TOMBSTONE;
513                     table[index + 1] = null;
514                     tombstones++;
515                     size--;
516                     return;
517                 }
518 
519                 if (reference == null) {
520                     // No entry found.
521                     return;
522                 }
523             }
524         }
525 
526         /**
527          * Gets the next index. If we're at the end of the table, we wrap back
528          * around to 0.
529          */
530         private int next(int index) {
531             return (index + 2) & mask;
532         }
533     }
534 }
View Code

 简单的看一下Java源码,我们就会发现,你set一个对象到ThreadLocal的时候,其实这个对象是被保存到当前线程的ThreadLocalMap里的,而每一个Thread里都包含一个ThreadLocal.ThreadLocalMap threadLocals = null;代码:

1     public void set(T value) {
2         Thread t = Thread.currentThread();
3         ThreadLocalMap map = getMap(t);
4         if (map != null)
5             map.set(this, value);
6         else
7             createMap(t, value);
8     }
1     ThreadLocalMap getMap(Thread t) {
2         return t.threadLocals;
3     }
 1 public
 2 class Thread implements Runnable {
 3     /* Make sure registerNatives is the first thing <clinit> does. */
 4     private static native void registerNatives();
 5     static {
 6         registerNatives();
 7     }
 8 
 9     private char    name[];
10     private int         priority;
11     private Thread    threadQ;
12     private long    eetop;
13     ...
14     /* ThreadLocal values pertaining to this thread. This map is maintained
15      * by the ThreadLocal class. */
16     ThreadLocal.ThreadLocalMap threadLocals = null;

重点看这句:

1 map.set(this, value);

可能有人要问了,如果只是简单的将value放到Map对象里面,那不可能起到每个线程各自保存一份对象的目的啊,我们继续看map.set(this, value)方法:

 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         }

看到了吧,不是简单的值传递,而是重新创建了一个对象并保存起来。然后get的时候当然就是线程自己创建的对象喽,其实实现原理很简单,不是吗?

发表评论
用户名: 匿名