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


    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

      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的时候当然就是线程自己创建的对象喽,其实实现原理很简单,不是吗?

  • 相关阅读:
    JUC-狂神笔记整理学习
    多线程-学习笔记
    Redis分布锁
    Redis
    springcloud一个简单的基本流程
    Nacos
    mysql单表查询
    mysql多表查询
    mysql数据库
    mysql详细安装教程以及1067错误代码解决方案
  • 原文地址:https://www.cnblogs.com/wlrhnh/p/3487904.html
Copyright © 2020-2023  润新知