zoukankan      html  css  js  c++  java
  • Java基础--ThreadLocal

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

    示例

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

    运行结果

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

    源码解析

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

     

  • 相关阅读:
    IntelliJ IDEA 常用快捷键和设置
    Code Project精彩系列(2)
    Code Project精彩系列(2)
    Code Project精彩系列(2)
    Apache和Tomcat区别
    Apache和Tomcat区别
    Apache和Tomcat区别
    如何在 GitHub 建立个人主页和项目演示页面
    如何在 GitHub 建立个人主页和项目演示页面
    如何在 GitHub 建立个人主页和项目演示页面
  • 原文地址:https://www.cnblogs.com/luangeng/p/5785527.html
Copyright © 2011-2022 走看看