ThreadLocal & Thread源码解读

ThreadLocal是什么?   

   ThreadLocal出现在JDK-1.2版本中,在1.5的时候进行了优化,加入了泛型数据。ThreadLocal是用来保存本线程数据信息的,跟我们平常所用到的线程本地存储-TLS(Thread Local Storage)是一个概念。为解决非共享对象的多线程并发问题提供了一种新的思路,因此需要明确的是它不是用来解决共享对象的多线程的访问问题的;解决共享对象的多线程一般使用锁或者原子操作之类的,是多个线程排队操作共享对象。

   ThreadLocal.set()到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,且其他线程也不能访问。

   ThreadLocal.get()方法执行时,各线程从自己的Values中去取出放进去的对象,因此取出的是各自自己放入到线程中的对象。

 

ThreadLocal源码及实现原理:

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

 

主要用到的两个方法:

public void set(T value);

public T get()
首先从set方法说起,相关代码如下:
1. 获取到当前的线程;
2. 获取当前线程的ThreadLocal的localValues值;localValues中保存一个数组,数组的索引是通过传入的hash值及数组的大小与运算而计算出来的。
3. 如果获取到的values值为空,调用initialzeValues(...)方法,此方法只有一句代码,current.localValues = new Values();
4. 存入值到values中去。put方法中做了很多少事情,需要判断是否要rehash等,
1 public void set(T value)  {
2     Thread currentThread = Thread.currentThread();
3     Values values = values(currentThread);
4     if (values == null) {
5           values = initializeValues(currentThread);
6      }
7      values.put(this, value);
8 }

 

get方法实现:
1. 前面两行代码同set方法,获取当前线程,并获取到当前线程的localValues对象。
2. 如果value不为空,则从数组中获取数据,如果当前数组保存的数据是当前线程中的ThreadLocal的引用,则返回table[index+1];
3. 如果values为空,先new 一个对象,返回values.getAfterMiss(this);
 1     public T get() {
 2         // Optimized for the fast path.
 3         Thread currentThread = Thread.currentThread();
 4         Values values = values(currentThread);
 5         if (values != null) {
 6             Object[] table = values.table;
 7             int index = hash & values.mask;
 8             if (this.reference == table[index]) {
 9                 return (T) table[index + 1];
10             }
11         } else {
12             values = initializeValues(currentThread);
13         }
14 
15         return (T) values.getAfterMiss(this);
16     }

 

总结:

  1.  Values中的table数组必须是2的幂次方,偶数地址上保存的reference,奇数地址上保存的是需要保存的值。

       2.  ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象

  

 

posted on 2016-06-21 12:50  宇智波.鼬  阅读(204)  评论(0)    收藏  举报

导航