Java基础--ThreadLocal
Java中的ThreadLocal 可以看做以线程标识为key的Map,在多线程开发中使用非常方便。
示例
 class ThreadEnv {
     // 用匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
     private static ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
         @Override
         protected Integer initialValue() {
             return 10;
         }
     };
     public int get() {
         // 第一次get到的是初始值
         int a = threadId.get();
         a++;
         threadId.set(a);
         return a;
     }
 }
 public class ThreadLocalTest {
     public static void main(String[] args) {
         ThreadEnv sn = new ThreadEnv();
         new TestClient(sn);
         new TestClient(sn);
         new TestClient(sn);
     }
     private static class TestClient extends Thread {
         private ThreadEnv sn;
         public TestClient(ThreadEnv sn) {
             this.sn = sn;
             this.start();
         }
         public void run() {
             for (int i = 0; i < 3; i++) {
                 System.out.println(Thread.currentThread()+ " >>> " + sn.get());
             }
         }
     }
 }
运行结果
Thread[Thread-,,main] >>>
Thread[Thread-,,main] >>>
Thread[Thread-,,main] >>>
Thread[Thread-,,main] >>>
Thread[Thread-,,main] >>>
Thread[Thread-,,main] >>>
Thread[Thread-,,main] >>>
Thread[Thread-,,main] >>>
Thread[Thread-,,main] >>>
源码解析
     public ThreadLocal() {
     }
     /**
      * Returns the value in the current thread's copy of this
      * thread-local variable.  If the variable has no value for the
      * current thread, it is first initialized to the value returned
      * by an invocation of the {@link #initialValue} method.
      *
      * @return the current thread's value of this thread-local
      */
     public T get() {
         Thread t = Thread.currentThread();
         ThreadLocalMap map = getMap(t);
         if (map != null) {
             ThreadLocalMap.Entry e = map.getEntry(this);
             if (e != null) {
                 @SuppressWarnings("unchecked")
                 T result = (T)e.value;
                 return result;
             }
         }
         return setInitialValue();
     }
     /**
      * Variant of set() to establish initialValue. Used instead
      * of set() in case user has overridden the set() method.
      *
      * @return the initial value
      */
     private T setInitialValue() {
         T value = initialValue();
         Thread t = Thread.currentThread();
         ThreadLocalMap map = getMap(t);
         if (map != null)
             map.set(this, value);
         else
             createMap(t, value);
         return value;
     }
     /**
      * Sets the current thread's copy of this thread-local variable
      * to the specified value.  Most subclasses will have no need to
      * override this method, relying solely on the {@link #initialValue}
      * method to set the values of thread-locals.
      *
      * @param value the value to be stored in the current thread's copy of
      *        this thread-local.
      */
     public void set(T value) {
         Thread t = Thread.currentThread();
         ThreadLocalMap map = getMap(t);
         if (map != null)
             map.set(this, value);
         else
             createMap(t, value);
     }
     /**
      * Removes the current thread's value for this thread-local
      * variable.  If this thread-local variable is subsequently
      * {@linkplain #get read} by the current thread, its value will be
      * reinitialized by invoking its {@link #initialValue} method,
      * unless its value is {@linkplain #set set} by the current thread
      * in the interim.  This may result in multiple invocations of the
      * {@code initialValue} method in the current thread.
      *
      * @since 1.5
      */
      public void remove() {
          ThreadLocalMap m = getMap(Thread.currentThread());
          if (m != null)
              m.remove(this);
      }
     /**
      * Get the map associated with a ThreadLocal. Overridden in
      * InheritableThreadLocal.
      *
      * @param  t the current thread
      * @return the map
      */
     ThreadLocalMap getMap(Thread t) {
         return t.threadLocals;
     }
     /**
      * Create the map associated with a ThreadLocal. Overridden in
      * InheritableThreadLocal.
      *
      * @param t the current thread
      * @param firstValue value for the initial entry of the map
      */
     void createMap(Thread t, T firstValue) {
         t.threadLocals = new ThreadLocalMap(this, firstValue);
     }
     /**
      * Factory method to create map of inherited thread locals.
      * Designed to be called only from Thread constructor.
      *
      * @param  parentMap the map associated with parent thread
      * @return a map containing the parent's inheritable bindings
      */
     static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
         return new ThreadLocalMap(parentMap);
     }
     /**
      * Method childValue is visibly defined in subclass
      * InheritableThreadLocal, but is internally defined here for the
      * sake of providing createInheritedMap factory method without
      * needing to subclass the map class in InheritableThreadLocal.
      * This technique is preferable to the alternative of embedding
      * instanceof tests in methods.
      */
     T childValue(T parentValue) {
         throw new UnsupportedOperationException();
     }
     /**
      * An extension of ThreadLocal that obtains its initial value from
      * the specified {@code Supplier}.
      */
     static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
         private final Supplier<? extends T> supplier;
         SuppliedThreadLocal(Supplier<? extends T> supplier) {
             this.supplier = Objects.requireNonNull(supplier);
         }
         @Override
         protected T initialValue() {
             return supplier.get();
         }
     }
     /**
      * ThreadLocalMap is a customized hash map suitable only for
      * maintaining thread local values. No operations are exported
      * outside of the ThreadLocal class. The class is package private to
      * allow declaration of fields in class Thread.  To help deal with
      * very large and long-lived usages, the hash table entries use
      * WeakReferences for keys. However, since reference queues are not
      * used, stale entries are guaranteed to be removed only when
      * the table starts running out of space.
      */
     static class ThreadLocalMap {
         /**
          * The entries in this hash map extend WeakReference, using
          * its main ref field as the key (which is always a
          * ThreadLocal object).  Note that null keys (i.e. entry.get()
          * == null) mean that the key is no longer referenced, so the
          * entry can be expunged from table.  Such entries are referred to
          * as "stale entries" in the code that follows.
          */
         static class Entry extends WeakReference<ThreadLocal<?>> {
             /** The value associated with this ThreadLocal. */
             Object value;
             Entry(ThreadLocal<?> k, Object v) {
                 super(k);
                 value = v;
             }
         }
         /**
          * The initial capacity -- MUST be a power of two.
          */
         private static final int INITIAL_CAPACITY = 16;
         /**
          * The table, resized as necessary.
          * table.length MUST always be a power of two.
          */
         private Entry[] table;
         /**
          * The number of entries in the table.
          */
         private int size = 0;
         /**
          * The next size value at which to resize.
          */
         private int threshold; // Default to 0
         /**
          * Set the resize threshold to maintain at worst a 2/3 load factor.
          */
         private void setThreshold(int len) {
             threshold = len * 2 / 3;
         }
         /**
          * Increment i modulo len.
          */
         private static int nextIndex(int i, int len) {
             return ((i + 1 < len) ? i + 1 : 0);
         }
         /**
          * Decrement i modulo len.
          */
         private static int prevIndex(int i, int len) {
             return ((i - 1 >= 0) ? i - 1 : len - 1);
         }
         /**
          * Construct a new map initially containing (firstKey, firstValue).
          * ThreadLocalMaps are constructed lazily, so we only create
          * one when we have at least one entry to put in it.
          */
         ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
             table = new Entry[INITIAL_CAPACITY];
             int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
             table[i] = new Entry(firstKey, firstValue);
             size = 1;
             setThreshold(INITIAL_CAPACITY);
         }
         /**
          * Construct a new map including all Inheritable ThreadLocals
          * from given parent map. Called only by createInheritedMap.
          *
          * @param parentMap the map associated with parent thread.
          */
         private ThreadLocalMap(ThreadLocalMap parentMap) {
             Entry[] parentTable = parentMap.table;
             int len = parentTable.length;
             setThreshold(len);
             table = new Entry[len];
             for (int j = 0; j < len; j++) {
                 Entry e = parentTable[j];
                 if (e != null) {
                     @SuppressWarnings("unchecked")
                     ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                     if (key != null) {
                         Object value = key.childValue(e.value);
                         Entry c = new Entry(key, value);
                         int h = key.threadLocalHashCode & (len - 1);
                         while (table[h] != null)
                             h = nextIndex(h, len);
                         table[h] = c;
                         size++;
                     }
                 }
             }
         }
         /**
          * Get the entry associated with key.  This method
          * itself handles only the fast path: a direct hit of existing
          * key. It otherwise relays to getEntryAfterMiss.  This is
          * designed to maximize performance for direct hits, in part
          * by making this method readily inlinable.
          *
          * @param  key the thread local object
          * @return the entry associated with key, or null if no such
          */
         private Entry getEntry(ThreadLocal<?> key) {
             int i = key.threadLocalHashCode & (table.length - 1);
             Entry e = table[i];
             if (e != null && e.get() == key)
                 return e;
             else
                 return getEntryAfterMiss(key, i, e);
         }
         /**
          * Version of getEntry method for use when key is not found in
          * its direct hash slot.
          *
          * @param  key the thread local object
          * @param  i the table index for key's hash code
          * @param  e the entry at table[i]
          * @return the entry associated with key, or null if no such
          */
         private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
             Entry[] tab = table;
             int len = tab.length;
             while (e != null) {
                 ThreadLocal<?> k = e.get();
                 if (k == key)
                     return e;
                 if (k == null)
                     expungeStaleEntry(i);
                 else
                     i = nextIndex(i, len);
                 e = tab[i];
             }
             return null;
         }
         /**
          * Set the value associated with key.
          *
          * @param key the thread local object
          * @param value the value to be set
          */
         private void set(ThreadLocal<?> key, Object value) {
             // We don't use a fast path as with get() because it is at
             // least as common to use set() to create new entries as
             // it is to replace existing ones, in which case, a fast
             // path would fail more often than not.
             Entry[] tab = table;
             int len = tab.length;
             int i = key.threadLocalHashCode & (len-1);
             for (Entry e = tab[i];
                  e != null;
                  e = tab[i = nextIndex(i, len)]) {
                 ThreadLocal<?> k = e.get();
                 if (k == key) {
                     e.value = value;
                     return;
                 }
                 if (k == null) {
                     replaceStaleEntry(key, value, i);
                     return;
                 }
             }
             tab[i] = new Entry(key, value);
             int sz = ++size;
             if (!cleanSomeSlots(i, sz) && sz >= threshold)
                 rehash();
         }
         /**
          * Remove the entry for key.
          */
         private void remove(ThreadLocal<?> key) {
             Entry[] tab = table;
             int len = tab.length;
             int i = key.threadLocalHashCode & (len-1);
             for (Entry e = tab[i];
                  e != null;
                  e = tab[i = nextIndex(i, len)]) {
                 if (e.get() == key) {
                     e.clear();
                     expungeStaleEntry(i);
                     return;
                 }
             }
         }
         /**
          * Replace a stale entry encountered during a set operation
          * with an entry for the specified key.  The value passed in
          * the value parameter is stored in the entry, whether or not
          * an entry already exists for the specified key.
          *
          * As a side effect, this method expunges all stale entries in the
          * "run" containing the stale entry.  (A run is a sequence of entries
          * between two null slots.)
          *
          * @param  key the key
          * @param  value the value to be associated with key
          * @param  staleSlot index of the first stale entry encountered while
          *         searching for key.
          */
         private void replaceStaleEntry(ThreadLocal<?> key, Object value,
                                        int staleSlot) {
             Entry[] tab = table;
             int len = tab.length;
             Entry e;
             // Back up to check for prior stale entry in current run.
             // We clean out whole runs at a time to avoid continual
             // incremental rehashing due to garbage collector freeing
             // up refs in bunches (i.e., whenever the collector runs).
             int slotToExpunge = staleSlot;
             for (int i = prevIndex(staleSlot, len);
                  (e = tab[i]) != null;
                  i = prevIndex(i, len))
                 if (e.get() == null)
                     slotToExpunge = i;
             // Find either the key or trailing null slot of run, whichever
             // occurs first
             for (int i = nextIndex(staleSlot, len);
                  (e = tab[i]) != null;
                  i = nextIndex(i, len)) {
                 ThreadLocal<?> k = e.get();
                 // If we find key, then we need to swap it
                 // with the stale entry to maintain hash table order.
                 // The newly stale slot, or any other stale slot
                 // encountered above it, can then be sent to expungeStaleEntry
                 // to remove or rehash all of the other entries in run.
                 if (k == key) {
                     e.value = value;
                     tab[i] = tab[staleSlot];
                     tab[staleSlot] = e;
                     // Start expunge at preceding stale entry if it exists
                     if (slotToExpunge == staleSlot)
                         slotToExpunge = i;
                     cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
                     return;
                 }
                 // If we didn't find stale entry on backward scan, the
                 // first stale entry seen while scanning for key is the
                 // first still present in the run.
                 if (k == null && slotToExpunge == staleSlot)
                     slotToExpunge = i;
             }
             // If key not found, put new entry in stale slot
             tab[staleSlot].value = null;
             tab[staleSlot] = new Entry(key, value);
             // If there are any other stale entries in run, expunge them
             if (slotToExpunge != staleSlot)
                 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
         }
         /**
          * Expunge a stale entry by rehashing any possibly colliding entries
          * lying between staleSlot and the next null slot.  This also expunges
          * any other stale entries encountered before the trailing null.  See
          * Knuth, Section 6.4
          *
          * @param staleSlot index of slot known to have null key
          * @return the index of the next null slot after staleSlot
          * (all between staleSlot and this slot will have been checked
          * for expunging).
          */
         private int expungeStaleEntry(int staleSlot) {
             Entry[] tab = table;
             int len = tab.length;
             // expunge entry at staleSlot
             tab[staleSlot].value = null;
             tab[staleSlot] = null;
             size--;
             // Rehash until we encounter null
             Entry e;
             int i;
             for (i = nextIndex(staleSlot, len);
                  (e = tab[i]) != null;
                  i = nextIndex(i, len)) {
                 ThreadLocal<?> k = e.get();
                 if (k == null) {
                     e.value = null;
                     tab[i] = null;
                     size--;
                 } else {
                     int h = k.threadLocalHashCode & (len - 1);
                     if (h != i) {
                         tab[i] = null;
                         // Unlike Knuth 6.4 Algorithm R, we must scan until
                         // null because multiple entries could have been stale.
                         while (tab[h] != null)
                             h = nextIndex(h, len);
                         tab[h] = e;
                     }
                 }
             }
             return i;
         }
         /**
          * Heuristically scan some cells looking for stale entries.
          * This is invoked when either a new element is added, or
          * another stale one has been expunged. It performs a
          * logarithmic number of scans, as a balance between no
          * scanning (fast but retains garbage) and a number of scans
          * proportional to number of elements, that would find all
          * garbage but would cause some insertions to take O(n) time.
          *
          * @param i a position known NOT to hold a stale entry. The
          * scan starts at the element after i.
          *
          * @param n scan control: {@code log2(n)} cells are scanned,
          * unless a stale entry is found, in which case
          * {@code log2(table.length)-1} additional cells are scanned.
          * When called from insertions, this parameter is the number
          * of elements, but when from replaceStaleEntry, it is the
          * table length. (Note: all this could be changed to be either
          * more or less aggressive by weighting n instead of just
          * using straight log n. But this version is simple, fast, and
          * seems to work well.)
          *
          * @return true if any stale entries have been removed.
          */
         private boolean cleanSomeSlots(int i, int n) {
             boolean removed = false;
             Entry[] tab = table;
             int len = tab.length;
             do {
                 i = nextIndex(i, len);
                 Entry e = tab[i];
                 if (e != null && e.get() == null) {
                     n = len;
                     removed = true;
                     i = expungeStaleEntry(i);
                 }
             } while ( (n >>>= 1) != 0);
             return removed;
         }
         /**
          * Re-pack and/or re-size the table. First scan the entire
          * table removing stale entries. If this doesn't sufficiently
          * shrink the size of the table, double the table size.
          */
         private void rehash() {
             expungeStaleEntries();
             // Use lower threshold for doubling to avoid hysteresis
             if (size >= threshold - threshold / 4)
                 resize();
         }
         /**
          * Double the capacity of the table.
          */
         private void resize() {
             Entry[] oldTab = table;
             int oldLen = oldTab.length;
             int newLen = oldLen * 2;
             Entry[] newTab = new Entry[newLen];
             int count = 0;
             for (int j = 0; j < oldLen; ++j) {
                 Entry e = oldTab[j];
                 if (e != null) {
                     ThreadLocal<?> k = e.get();
                     if (k == null) {
                         e.value = null; // Help the GC
                     } else {
                         int h = k.threadLocalHashCode & (newLen - 1);
                         while (newTab[h] != null)
                             h = nextIndex(h, newLen);
                         newTab[h] = e;
                         count++;
                     }
                 }
             }
             setThreshold(newLen);
             size = count;
             table = newTab;
         }
         /**
          * Expunge all stale entries in the table.
          */
         private void expungeStaleEntries() {
             Entry[] tab = table;
             int len = tab.length;
             for (int j = 0; j < len; j++) {
                 Entry e = tab[j];
                 if (e != null && e.get() == null)
                     expungeStaleEntry(j);
             }
         }
     }
 }
Java基础--ThreadLocal的更多相关文章
- java基础解析系列(七)---ThreadLocal原理分析
		java基础解析系列(七)---ThreadLocal原理分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)-- ... 
- Java基础(1)——ThreadLocal
		1. Java基础(1)--ThreadLocal 1.1. ThreadLocal ThreadLocal是一个泛型类,当我们在一个类中声明一个字段:private ThreadLocal<F ... 
- java基础之ThreadLocal
		早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编写出优美的多线程程序.Thr ... 
- [Java面经]干货整理, Java面试题(覆盖Java基础,Java高级,JavaEE,数据库,设计模式等)
		如若转载请注明出处: http://www.cnblogs.com/wang-meng/p/5898837.html 谢谢.上一篇发了一个找工作的面经, 找工作不宜, 希望这一篇的内容能够帮助到大 ... 
- Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)
		线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ... 
- java基础-java核心知识库
		本人从事java开发6年左右,主要从事互联网相关的开发,目前还是奋战在一线的码农,痛并快乐着.受互联网产品热潮的影响,关注高性能低成本架构,互联网开发框架,以下是我认为作为一个资深java程序员应该掌 ... 
- 【java基础之jdk源码】Object
		最新在整体回归下java基础薄弱环节,以下为自己整理笔记,若有理解错误,请批评指正,谢谢. java.lang.Object为java所有类的基类,所以一般的类都可用重写或直接使用Object下方法, ... 
- java基础解析系列(八)---fail-fast机制及CopyOnWriteArrayList的原理
		fail-fast机制及CopyOnWriteArrayList的原理 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列( ... 
- java基础解析系列(九)---String不可变性分析
		java基础解析系列(九)---String不可变性分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---In ... 
随机推荐
- 大话设计模式之PHP篇 - 简单工厂模式
			假设有一道编程题:输入两个数字和运算符,然后得到运算结果.非常简单的一道题目,通常的实现代码如下: <?php Function Operation($val1, $val2, $operate ... 
- Docker 搭建一个Docker应用栈
			Docker应用栈结构图 Build Django容器 编写docker-file FROM django RUN pip install redis build django-with-redis ... 
- JQuery 操作 iframe
			JQuery访问iframe内的元素 $("iframe#Main", top.document).contents().find("#id"); JQuery ... 
- relativePath
			比如: com.tenace tenace 2.0.1 ../pom.xml //刚开始无此句 com.spider engine 2.6.0-SNAPSHOT tenace作为pom项目已经发布至r ... 
- windows编译hadoop 2.x Hadoop-eclipse-plugin插件
			本文转载至:http://blog.csdn.net/congcong68/article/details/42098391 一.简介 Hadoop2.x之后没有Eclipse插件工具,我们就不能在E ... 
- HDU 5992 kd-tree
			还记得青岛的时候和蕾姐讨论了近三个小时也不知道这是什么东西 后来发现是kdtree 于是拖到寒假才补这个算法 写完几道模板题发现多维的kdtree查找最近也是很简单的拓展 于是很快1A了这道题 它真的 ... 
- QT QStringListModel 示例代码
			1. QStringListModel , 实现 插入 删除 编辑 list,支持鼠标双击编辑. 2. dialog.h #ifndef DIALOG_H #define DIALOG_H #inc ... 
- Linux嵌入式 -- 内核 - 系统调用
			1. 系统调用 定义 Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用.用户可以通过系统调用命令在自己的应用程序中调用它们. 系统调用和普通的函数调用非常相似,区别仅仅在于,系统调 ... 
- Grid_自绘
			ZC: 测试使用的控件是 Delphi7版本的 TAdvStringGrid(第3方控件) “DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: ... 
- myeclipse发布项目
			最好不要点击restart 虽然点击run,也会发布,但是最好先点击发布. 点击restart,可能会出现tomcat关不掉又启动不了. 
