Entry类继承了WeakReference<ThreadLocal<?>>,即每一个Entry对象都有一个ThreadLocal的弱引用(做为key),这是为了防止内存泄露。html
key变为一个不可达的对象(null),这个Entry就能够被GC了。算法
1 static class ThreadLocalMap { 2 3 /** 4 * The entries in this hash map extend WeakReference, using 5 * its main ref field as the key (which is always a 6 * ThreadLocal object). Note that null keys (i.e. entry.get() 7 * == null) mean that the key is no longer referenced, so the 8 * entry can be expunged from table. Such entries are referred to 9 * as "stale entries" in the code that follows. 10 */ 11 static class Entry extends WeakReference<ThreadLocal> { 12 /** The value associated with this ThreadLocal. */ 13 Object value; 14 15 Entry(ThreadLocal k, Object v) { 16 super(k); 17 value = v; 18 } 19 }
/*** 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); }
ThreadLocalMap构造函数的第一个参数就是该ThreadLocal实例(this),第二个参数就是要保存的线程本地变量。数据库
构造函数首先建立一个长度为16的Entry数组,而后计算出firstKey对应的哈希值,而后存储到table中,并设置size和threshold。数组
注意一个细节,计算hash的时候里面采用了hashCode & (size - 1)的算法,这至关于取模运算hashCode % size的一个更高效的实现(和安全
HashMap中的思路相同)。正是由于这种算法,咱们要求size必须是2的指数,由于这可使得hash发生冲突的次数减少。多线程
1 /* ThreadLocal values pertaining to this thread. This map is maintained 2 * by the ThreadLocal class. */ 3 ThreadLocal.ThreadLocalMap threadLocals = null; 4 5 /* 6 * InheritableThreadLocal values pertaining to this thread. This map is 7 * maintained by the InheritableThreadLocal class. 8 */ 9 ThreadLocal.ThreadLocalMap inheritableThreadLocals = 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); //以ThreadLocal为键 6 else 7 createMap(t, value); 8 } 9 void createMap(Thread t, T firstValue) { 10 t.threadLocals = new ThreadLocalMap(this, firstValue); //以ThreadLocal为键
11 }
ThreadLocal的get()方法并发
1 public T get() { 2 Thread t = Thread.currentThread(); //获得当前线程 3 ThreadLocalMap map = getMap(t); //获得当前线程相关的ThreadLocalMap 4 if (map != null) { 5 ThreadLocalMap.Entry e = map.getEntry(this); 6 if (e != null) 7 return (T)e.value; 8 } 9 return setInitialValue(); 10 } 11 12 ThreadLocalMap getMap(Thread t) { 13 return t.threadLocals; 14 }
ThreadLocal在Hibernate中的应用less
1 private static final ThreadLocal threadSession = new ThreadLocal(); 2 3 public static Session getSession() throws InfrastructureException { 4 Session s = (Session) threadSession.get(); 5 try { 6 if (s == null) { 7 s = getSessionFactory().openSession(); 8 threadSession.set(s); 9 } 10 } catch (HibernateException ex) { 11 throw new InfrastructureException(ex); 12 } 13 return s; 14 }
咱们在多线程的开发中,常常会考虑到的策略是对一些须要公开访问的属性经过设置同步的方式来访问。这样每次能保证只有一个线程访问它,不会有冲突。函数
可是这样作的结果会使得性能和对高并发的支持不够。在某些状况下,若是咱们不必定非要对一个变量共享不可,而是给每一个线程一个这样的资源副本,让他们可高并发
以独立都各自跑各自的,这样不是能够大幅度的提升并行度和性能了吗?
还有的状况是有的数据自己不是线程安全的,或者说它只能被一个线程使用,不能被其余线程同时使用。若是等一个线程使用完了再给另一个线程使用就
根本不现实。这样的状况下,咱们也能够考虑用ThreadLocal。一个典型的状况就是咱们链接数据库的时候一般会用到链接池。而对数据库的链接不能有多个线程
共享访问。这个时候就须要使用ThreadLocal了。
1 private static ThreadLocal<Connection> connectionHolder = 2 new ThreadLocal<Connection>() { 3 public Connection initialValue() { 4 return DriverManager.getConnection(DB_URL); 5 } 6 }; 7 8 9 pubic static Connection getConnection() { 10 return connectionHolder.get(); 11 }
ThreadLocal对象建议使用static修饰。这个变量是针对一个线程内全部操做共有的,
因此设置为静态变量,全部此类实例共享此静态变量 ,也就是说在类第一次被使用时装载,只分配一块存储空间
ThreadLocal
instances are typically private static fields in classes that wish to associate state with a thread
(e.g., a user ID or Transaction ID).
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal
instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection
(unless other references to these copies exist).
参考: