- 浏览: 981049 次
文章分类
- 全部博客 (428)
- Hadoop (2)
- HBase (1)
- ELK (1)
- ActiveMQ (13)
- Kafka (5)
- Redis (14)
- Dubbo (1)
- Memcached (5)
- Netty (56)
- Mina (34)
- NIO (51)
- JUC (53)
- Spring (13)
- Mybatis (17)
- MySQL (21)
- JDBC (12)
- C3P0 (5)
- Tomcat (13)
- SLF4J-log4j (9)
- P6Spy (4)
- Quartz (12)
- Zabbix (7)
- JAVA (9)
- Linux (15)
- HTML (9)
- Lucene (0)
- JS (2)
- WebService (1)
- Maven (4)
- Oracle&MSSQL (14)
- iText (11)
- Development Tools (8)
- UTILS (4)
- LIFE (8)
最新评论
-
Donald_Draper:
Donald_Draper 写道刘落落cici 写道能给我发一 ...
DatagramChannelImpl 解析三(多播) -
Donald_Draper:
刘落落cici 写道能给我发一份这个类的源码吗Datagram ...
DatagramChannelImpl 解析三(多播) -
lyfyouyun:
请问楼主,执行消息发送的时候,报错:Transport sch ...
ActiveMQ连接工厂、连接详解 -
ezlhq:
关于 PollArrayWrapper 状态含义猜测:参考 S ...
WindowsSelectorImpl解析一(FdMap,PollArrayWrapper) -
flyfeifei66:
打算使用xmemcache作为memcache的客户端,由于x ...
Memcached分布式客户端(Xmemcached)
Spring+Mybatis多数据源的实现:http://donald-draper.iteye.com/blog/2326034
在上面一篇文章中我们在数据源切换的过程中用到过ThreadLocal,用ThreadLocal数据源名,主要是
保证每个线程的数据源相互独立,互不干扰。ThreadLocal声明的变量保证每个线程拥有一个独立的副本;以前的无论是Redis还是ActiveMQ系列篇中,凡是有数据源事务关联的我们都看到ThreadLocal,主要是保证每个线程的事务独立性,避免事务交叉。今天我们就来看看ThreadLocal源码。
//ThreadLocal
我们一般用ThreadLocal为如下形式
ThreadLocal一般被声明为 private static final。
ThreadLocal主要要set,get和remove,3个主要方法。下面我们分别来看:
先看set方法;
这个方法有3点要关注为:
1.
2.
3.
下面我们分别来看这几点,先看第3点:
上面这一段是线程的内部变量threadLocals,inheritableThreadLocals;
threadLocals保存线程本地变量,inheritableThreadLocals保存从父类继承的线程本地变量。在往下看之前我们来看一下ThreadLocalMap的定义:
回到set方法的几个关键点
1.
2.
这里有节点要关注:
a.
b.
c.
下面分别来看这几点:
a.
b.
从方法来看replaceStaleEntry所有做的工作,从当前Stale Entry的位置staleSlot,
遍历找到最前面的Stale Entry的位置slotToExpunge,并向后遍历Entry如果有Key相等的,
则与staleSlot交换Entry,即将stale Entry往table后面移。如果Stale Entry的位置staleSlot前面没有stale Entry,则slotToExpunge为当前位置(staleSlot后第一个与key相等的),移除当前位置Entry,并重新冲突的Entry。如果staleSlot的Entry为stale状态,则移除。
先来看一下向前移动位置:
再来看移除stale Entry,解决冲突的Entry。
c.
来看重hash
至此ThreadLocal的set方法讲解完毕,小节一下:
每个线程拥有一个线程本地变量ThreadLocalMap-threadLocals和一个可继承的ThreadLocalMap-inheritableThreadLocals。每个ThreadLocal关联一个threadLocalHashCode,在设值ThreadLocal时,获取当前线程的线程本地变量ThreadLocalMap-threadLocals,如果为空,则初始化当前线程的threadLocals,
即创建一个ThreadLocalMap,并将TheadLocal的threadLocalHashCode与value的映射Entry添加到threadLocals中,如果当前线程的threadLocals不为null,则添加TheadLocal的threadLocalHashCode与value的映射Entry。
再看看get方法:
//先看从Map获取值
//ThreadLocalMap
再来看初始化TheadLocal
从上面来看get方法,首先获取当前线程的线程本地变量Map-threadLocals,如果不为空,则从Map中,获取ThreadLocal的threadLocalHashCode对应Entry,返回对应的值,如果threadLocals为null,首先初始化ThreadLocal的值,然后重新检查
threadLocals是否为null,后面的与set的方法相同,就不说了。
再来看remove方法:
//ThreadLocalMap
我们来看参考清除
移除操作,主要是从当前线程的threadLocals移除对应的TheadLocal,并清除TheadLocal的引用。
再看创建可继承的线程本地变量方法:
//ThreadLocalMap
总结:
每个线程拥有一个线程本地变量ThreadLocalMap-threadLocals和一个可继承的ThreadLocalMap-inheritableThreadLocals。每个ThreadLocal关联一个threadLocalHashCode,在set设值ThreadLocal时,获取当前线程的线程本地变量ThreadLocalMap-threadLocals,如果为空,则初始化当前线程的threadLocals,
即创建一个ThreadLocalMap,并将TheadLocal的threadLocalHashCode与value的映射Entry添加到threadLocals中,如果当前线程的threadLocals不为null,则添加TheadLocal的threadLocalHashCode与value的映射Entry。get方法,首先获取当前线程的线程本地变量Map-threadLocals,如果不为空,则从Map中,获取ThreadLocal的threadLocalHashCode对应Entry,返回对应的值,如果threadLocals为null,首先初始化ThreadLocal的值,然后重新检查threadLocals是否为null,后面的与set的方法相同,就不说了。移除操作,主要是从当前线程的threadLocals移除对应的TheadLocal,并清除TheadLocal的引用。
最后我们再来看一下可继承的线程本地变量InheritableThreadLocal
//InheritableThreadLocal
实例:
//WeakReference
Reference:
在上面一篇文章中我们在数据源切换的过程中用到过ThreadLocal,用ThreadLocal数据源名,主要是
保证每个线程的数据源相互独立,互不干扰。ThreadLocal声明的变量保证每个线程拥有一个独立的副本;以前的无论是Redis还是ActiveMQ系列篇中,凡是有数据源事务关联的我们都看到ThreadLocal,主要是保证每个线程的事务独立性,避免事务交叉。今天我们就来看看ThreadLocal源码。
//ThreadLocal
package java.lang; import java.lang.ref.*; import java.util.concurrent.atomic.AtomicInteger; /** * This class provides thread-local variables. These variables differ from * their normal counterparts in that each thread that accesses one (via its * <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized * copy of the variable. <tt>ThreadLocal</tt> instances are typically private * static fields in classes that wish to associate state with a thread (e.g., * a user ID or Transaction ID). * ThreadLocal提供线程本地变量。这些变量不同于线程一般用get和set方法获取的变量, 它是一个独立的变量初始化拷贝。ThreadLocal实例是典型的私有静态访问fields,比如 我们希望每个线程关联一个状态,如用户或事物的ID * <p>For example, the class below generates unique identifiers local to each * thread. * A thread's id is assigned the first time it invokes <tt>ThreadId.get()</tt> * and remains unchanged on subsequent calls. 举个例子,ThreadId保证了每个线程用于一个本地唯一的标识。如果一个线程的id,在以第一次调用 ThreadId.get()方法时指定,接下来将不能改变 * <pre> * import java.util.concurrent.atomic.AtomicInteger; * * public class ThreadId { * // Atomic integer containing the next thread ID to be assigned * private static final AtomicInteger nextId = new AtomicInteger(0); * * // Thread local variable containing each thread's ID * private static final ThreadLocal<Integer> threadId = * new ThreadLocal<Integer>() { * @Override protected Integer initialValue() { * return nextId.getAndIncrement(); * } * }; * * // Returns the current thread's unique ID, assigning it if necessary * public static int get() { * return threadId.get(); * } * } * </pre> * <p>Each thread holds an implicit reference to its copy of a thread-local * variable as long as the thread is alive and the <tt>ThreadLocal</tt> * 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). * 只要线程存活,每个线程将拥有一个隐式的线程本地变量ThreadLocal的副本,在一个线程结束 之后所有的线程本地变量将会被垃圾回收器回收,除非有其他副本引用。 * @author Josh Bloch and Doug Lea * @since 1.2 */ public class ThreadLocal<T> { /** * ThreadLocals rely on per-thread linear-probe hash maps attached * to each thread (Thread.threadLocals and * inheritableThreadLocals). The ThreadLocal objects act as keys, * searched via threadLocalHashCode. This is a custom hash code * (useful only within ThreadLocalMaps) that eliminates collisions * in the common case where consecutively constructed ThreadLocals * are used by the same threads, while remaining well-behaved in * less common cases. ThreadLocals依靠每个线程的哈希Map将线程本地变量(Thread.threadLocals and inheritableThreadLocals)保存在每个线程中。ThreadLocal作为Entry的Key我们 可以通过threadLocalHashCode查找。这个哈希值只对ThreadLocalMaps有用,用于 排除在相同的线程中构造ThreadLocals引起的冲突, */ private final int threadLocalHashCode = nextHashCode(); /** * The next hash code to be given out. Updated atomically. Starts at * zero. 用于计算下一个哈希值 */ private static AtomicInteger nextHashCode = new AtomicInteger(); /** * The difference between successively generated hash codes - turns * implicit sequential thread-local IDs into near-optimally spread * multiplicative hash values for power-of-two-sized tables. //哈希值增长步长,计算ThreadLocal的ID */ private static final int HASH_INCREMENT = 0x61c88647; /** * Returns the next hash code. 返回ThreadLocal的下一个hashCode,先get后add */ private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); } /** * Creates a thread local variable. */ public ThreadLocal() { } }
我们一般用ThreadLocal为如下形式
/** * 数据源上下文 * @author donald * */ public class DataSourceContextHolder { public final static String DATA_SOURCE_LOCAL = "dataSource"; public final static String DATA_SOURCE_SYNC = "syncDataSource"; //对数据源名,线程隔离 private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setDataSourceType(String dataSource) { contextHolder.set(dataSource); } public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } }
ThreadLocal一般被声明为 private static final。
ThreadLocal主要要set,get和remove,3个主要方法。下面我们分别来看:
先看set方法;
/** * 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. * 设置当前线程本地变量的拷贝为特定的值。大多数的子类不需要重写此方法, 只需要单独重写#initialValue设置线程本地变量值。 * @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 ThreadLocalMap map = getMap(t); if (map != null) //如果不为null,则将TheadLocal和对应的值放入ThreadLocalMap map.set(this, value); else //否则根据当前线程和对应的线程本地变量值创建ThreadLocalMap createMap(t, value); }
这个方法有3点要关注为:
1.
Thread t = Thread.currentThread(); //获取当前线程的ThreadLocalMap ThreadLocalMap map = getMap(t);
2.
if (map != null) //如果不为null,则将TheadLocal和对应的值放入ThreadLocalMap map.set(this, value);
3.
else //否则根据当前线程和对应的线程本地变量值创建ThreadLocalMap createMap(t, value);
下面我们分别来看这几点,先看第3点:
else //否则根据当前线程和对应的线程本地变量值创建ThreadLocalMap createMap(t, value);
/** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * 创建当前线程的ThreadLocalMap,并ThreadLocal和对应的value添加到ThreadLocalMap * @param t the current thread * @param firstValue value for the initial entry of the map * @param map the map to store. */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
//Thread /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null; /* * InheritableThreadLocal values pertaining to this thread. This map is * maintained by the InheritableThreadLocal class. */ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
上面这一段是线程的内部变量threadLocals,inheritableThreadLocals;
threadLocals保存线程本地变量,inheritableThreadLocals保存从父类继承的线程本地变量。在往下看之前我们来看一下ThreadLocalMap的定义:
/** * 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. ThreadLocalMap是用于维护线程本地变量的哈希Map。没够操作暴露给外部的类ThreadLocal。ThreadLocalMap是私有的内部类,允许在Thread中声明为fields。为了保证在大量长存活对象存在的Map的高效可利用性,我们用WeakReferences来保存key值。由于队列不在被引用,当table使用时,超时空间,可以保证那些stale(陈腐,即key为null)的Entry被移除。 */ 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. Entry扩展了WeakReference,用于维护key的引用。如果可以为null,表示key不在 被引用,可以从哈希表中移除。这样的Entry,我们在以下代码中称谓stale entries,。 */ 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. 初始化容量,必须为2的N次方 */ private static final int INITIAL_CAPACITY = 16; /** * The table, resized as necessary. * table.length MUST always be a power of two. 存放Entry的table,长度总是为2的n次方,如果需要重新扩容 */ private Entry[] table; /** * The number of entries in the table. table中的Entry数。 */ 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. 设置扩容因子为扩容临界条件的2/3 */ private void setThreshold(int len) { threshold = len * 2 / 3; } /** * 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,并将Entry(firstKey, firstValue)放入到hashMap中 */ ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; //获取ThreadLocal的table索引 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; //设置扩容临界条件为初始化容量的2/3 setThreshold(INITIAL_CAPACITY); } }
回到set方法的几个关键点
1.
Thread t = Thread.currentThread(); //获取当前线程的ThreadLocalMap ThreadLocalMap map = getMap(t);
/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * 直接返回线程的ThreadLocalMap * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
2.
if (map != null) //如果不为null,则将TheadLocal和对应的值放入ThreadLocalMap map.set(this, value);
/** * 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; //定位线程本地变量在ThreadLocalMap的table中的索引 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) { //如果ThreadLocal已经存在,则更新对应的值 e.value = value; return; } if (k == null) { //如果ThreadLocal不存在,则替换ThreadLocal replaceStaleEntry(key, value, i); return; } } //索引上的Entry为null,则创建一个新Entry添加table中 tab[i] = new Entry(key, value); int sz = ++size; //如果清除索引i之后table上stale Entry失败,且到达扩容条件,则扩容 if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
这里有节点要关注:
a.
nextIndex(i, len)
b.
if (k == null) { //如果ThreadLocal不存在,则替换ThreadLocal replaceStaleEntry(key, value, i); return; }
c.
//如果清除索引i之后table上stale Entry失败,且到达扩容条件,则扩容 if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash();
下面分别来看这几点:
a.
nextIndex(i, len)
/** * Increment i modulo len. //获取索引i的下一个table索引 */ private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); }
b.
if (k == null) { //如果ThreadLocal不存在,则替换ThreadLocal replaceStaleEntry(key, value, i); return; }
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; //向索引i之前遍历,找到第一个Entry的key为null的 for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) if (e.get() == null) //如果Entry对应的key,及ThreadLocal为null,记录索引 slotToExpunge = i; // Find either the key or trailing null slot of run, whichever // occurs first //向staleSlot之后遍历 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) { //如果key相等,则交换当前的Entry和之前Entry的key为null的stale entry, //即将stale Entry往table的后面移 e.value = value; tab[i] = tab[staleSlot]; tab[staleSlot] = e; // Start expunge at preceding stale entry if it exists //如果staleSlot之前没有stale Entry,则slotToExpune为i 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. //如果向后遍历的Entry的key为null,且staleSlot前面无stale Entry,则 //需要清除的Entry为当前Entry if (k == null && slotToExpunge == staleSlot) slotToExpunge = i; } 如果staleSlot之后的Entry为null,则直接将新放入的Entry添加到table的staleSlot位置上 // 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 //存在其他的stale Entry,则清除 if (slotToExpunge != staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); }
从方法来看replaceStaleEntry所有做的工作,从当前Stale Entry的位置staleSlot,
遍历找到最前面的Stale Entry的位置slotToExpunge,并向后遍历Entry如果有Key相等的,
则与staleSlot交换Entry,即将stale Entry往table后面移。如果Stale Entry的位置staleSlot前面没有stale Entry,则slotToExpunge为当前位置(staleSlot后第一个与key相等的),移除当前位置Entry,并重新冲突的Entry。如果staleSlot的Entry为stale状态,则移除。
先来看一下向前移动位置:
/** * Decrement i modulo len. */ private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 : len - 1); }
再来看移除stale Entry,解决冲突的Entry。
/** * 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 //移除staleSlot位置的Entry 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 { //遍历staleSlot之后的Entry,如果为stale,则移除,否则重新hash 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; }
c.
//如果清除索引i之后table上stale Entry失败,且到达扩容条件,则扩容 if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash();
//移除i位置之后的的stale Entry private boolean cleanSomeSlots(int i, int n) { boolean removed = false; Entry[] tab = table; int len = tab.length; //向后遍历table do { i = nextIndex(i, len); Entry e = tab[i]; if (e != null && e.get() == null) { n = len; //Entry为stale,则移除 removed = true; //移除i位置上的stale,并解决冲突 i = expungeStaleEntry(i); } } while ( (n >>>= 1) != 0); return removed; }
来看重hash
/** * 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() { //移除所有stale Entry expungeStaleEntries(); // Use lower threshold for doubling to avoid hysteresis if (size >= threshold - threshold / 4) resize(); } /** * Expunge all stale entries in the table. 移除所有stale Entry */ 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); } } /** * 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; }
至此ThreadLocal的set方法讲解完毕,小节一下:
每个线程拥有一个线程本地变量ThreadLocalMap-threadLocals和一个可继承的ThreadLocalMap-inheritableThreadLocals。每个ThreadLocal关联一个threadLocalHashCode,在设值ThreadLocal时,获取当前线程的线程本地变量ThreadLocalMap-threadLocals,如果为空,则初始化当前线程的threadLocals,
即创建一个ThreadLocalMap,并将TheadLocal的threadLocalHashCode与value的映射Entry添加到threadLocals中,如果当前线程的threadLocals不为null,则添加TheadLocal的threadLocalHashCode与value的映射Entry。
再看看get方法:
* 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. * 返回当前线程的线程本地变量的值。如果当前线程的线程本地变量Map为空,则初始化线程本地变量值。 * @return the current thread's value of this thread-local */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { //如果当线程的线程本地变量Map不为null,直接从Map中获取 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } //否则,初始化线程本地变量值 return setInitialValue(); }
//先看从Map获取值
//ThreadLocalMap
private Entry getEntry(ThreadLocal key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) //找到,则返回对应的Entry 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; //遍历i位置之后的Entry,找到key对应的Entry,则返回,如果Entry为stale,则移除 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; }
再来看初始化TheadLocal
/** * 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; } //待子类扩展 protected T initialValue() { return null; }
从上面来看get方法,首先获取当前线程的线程本地变量Map-threadLocals,如果不为空,则从Map中,获取ThreadLocal的threadLocalHashCode对应Entry,返回对应的值,如果threadLocals为null,首先初始化ThreadLocal的值,然后重新检查
threadLocals是否为null,后面的与set的方法相同,就不说了。
再来看remove方法:
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) //如果当前线程的threadLocals不为null,直接从threadLocals中移除ThreadLocal m.remove(this); }
//ThreadLocalMap
/** * Remove the entry for key. */ private void remove(ThreadLocal key) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); //遍历ThreadLocal位置之后的Entry,如果key相等,则移除Entry,并清除参考 for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) if (e.get() == key) { e.clear(); expungeStaleEntry(i); return; } } }
我们来看参考清除
e.clear();
//Reference /** * Clears this reference object. Invoking this method will not cause this * object to be enqueued. * * <p> This method is invoked only by Java code; when the garbage collector * clears references it does so directly, without invoking this method. */ public void clear() { this.referent = null; }
移除操作,主要是从当前线程的threadLocals移除对应的TheadLocal,并清除TheadLocal的引用。
再看创建可继承的线程本地变量方法:
/** * 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); }
//ThreadLocalMap
/** * 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) { ThreadLocal key = e.get(); if (key != null) { //获取Thread子线程的值 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++; } } } }
/** * 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(); }
总结:
每个线程拥有一个线程本地变量ThreadLocalMap-threadLocals和一个可继承的ThreadLocalMap-inheritableThreadLocals。每个ThreadLocal关联一个threadLocalHashCode,在set设值ThreadLocal时,获取当前线程的线程本地变量ThreadLocalMap-threadLocals,如果为空,则初始化当前线程的threadLocals,
即创建一个ThreadLocalMap,并将TheadLocal的threadLocalHashCode与value的映射Entry添加到threadLocals中,如果当前线程的threadLocals不为null,则添加TheadLocal的threadLocalHashCode与value的映射Entry。get方法,首先获取当前线程的线程本地变量Map-threadLocals,如果不为空,则从Map中,获取ThreadLocal的threadLocalHashCode对应Entry,返回对应的值,如果threadLocals为null,首先初始化ThreadLocal的值,然后重新检查threadLocals是否为null,后面的与set的方法相同,就不说了。移除操作,主要是从当前线程的threadLocals移除对应的TheadLocal,并清除TheadLocal的引用。
最后我们再来看一下可继承的线程本地变量InheritableThreadLocal
//InheritableThreadLocal
package java.lang; import java.lang.ref.*; /** * This class extends <tt>ThreadLocal</tt> to provide inheritance of values * from parent thread to child thread: when a child thread is created, the * child receives initial values for all inheritable thread-local variables * for which the parent has values. Normally the child's values will be * identical to the parent's; however, the child's value can be made an * arbitrary function of the parent's by overriding the <tt>childValue</tt> * method in this class. * InheritableThreadLocal继承了ThreadLocal,提供了子线程继承父线程本地变量的实现; 当子线程被创建,子线程将会拥有父线程所有可继承的线程本地变量。一般情况子线程的本地 变量值与父线程相同;如果子线程重写的父线程的childValue,将不能保证。 * <p>Inheritable thread-local variables are used in preference to * ordinary thread-local variables when the per-thread-attribute being * maintained in the variable (e.g., User ID, Transaction ID) must be * automatically transmitted to any child threads that are created. 当每个线程的属性保存在一个变量中如用户ID和事务ID,如果是可继承的线程本地变量 必须自动的传给所有创建的子线程,可继承的线程本地变量优先于一般的线程变量被使用。 * @author Josh Bloch and Doug Lea * @see ThreadLocal * @since 1.2 */ public class InheritableThreadLocal<T> extends ThreadLocal<T> { /** * Computes the child's initial value for this inheritable thread-local * variable as a function of the parent's value at the time the child * thread is created. This method is called from within the parent * thread before the child is started. 在子线程创建时,初始化子线程从父线程继承的线程本地变量。这个方法在子线程启动之前, 父线程调用。跟父线程的本地变量值,返回子线程的线程本地变量值,InheritableThreadLocal为 直接继承父类的值 * <p> * This method merely returns its input argument, and should be overridden * if a different behavior is desired. * * @param parentValue the parent thread's value * @return the child thread's initial value */ protected T childValue(T parentValue) { return parentValue; } /** * Get the map associated with a ThreadLocal. * 获取线程关联ThreadLocal * @param t the current thread */ ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; } /** * Create the map associated with a ThreadLocal. * 创建一个线程关联ThreadLocal的Map * @param t the current thread * @param firstValue value for the initial entry of the table. * @param map the map to store. */ void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); } }
实例:
public class ParentThread extends Thread { void run (){ ChildThread cThread = new ChildThread() cThread.inheritableThreadLocals = ThreadLocalMap.createInheritedMap(this.inheritableThreadLocals); } }
public class ChildThread extends Thread { void run (){ ... } }
//WeakReference
package java.lang.ref; /** * Weak reference objects, which do not prevent their referents from being * made finalizable, finalized, and then reclaimed. Weak references are most * often used to implement canonicalizing mappings. * * <p> Suppose that the garbage collector determines at a certain point in time * that an object is <a href="package-summary.html#reachability">weakly * reachable</a>. At that time it will atomically clear all weak references to * that object and all weak references to any other weakly-reachable objects * from which that object is reachable through a chain of strong and soft * references. At the same time it will declare all of the formerly * weakly-reachable objects to be finalizable. At the same time or at some * later time it will enqueue those newly-cleared weak references that are * registered with reference queues. * * @author Mark Reinhold * @since 1.2 */ public class WeakReference<T> extends Reference<T> { /** * Creates a new weak reference that refers to the given object. The new * reference is not registered with any queue. * * @param referent object the new weak reference will refer to */ public WeakReference(T referent) { super(referent); } /** * Creates a new weak reference that refers to the given object and is * registered with the given queue. * * @param referent object the new weak reference will refer to * @param q the queue with which the reference is to be registered, * or <tt>null</tt> if registration is not required */ public WeakReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); } }
Reference:
发表评论
-
Reference定义(PhantomReference,Cleaner)
2017-04-28 17:24 1276Java NIO ByteBuffer详解:http://do ... -
VIsualVM与MemoryAnalyzer分析堆内存过程
2017-02-25 14:06 6069背景:有个一数据库记录增量更新线程,运行过程中,吃内存较大,打 ... -
java虚拟机内存查看相关命令
2017-02-23 20:03 4184关于cmd命令的重定向输出 :http://blog.csdn ... -
Java动态代理
2016-12-01 10:49 555深入浅析Spring 的aop实现原理:http://www. ... -
Java Stack用法
2016-10-13 09:58 1598java.util.Stack类简介 :http://blog ... -
JVM,ConcurrentMarkSweep垃圾回收器实战分析
2016-10-11 18:37 1664堆内存设置:http://blog.csdn.net/sivy ... -
深入理解Collections的unmodifiableMap(Map map)方法
2016-09-23 08:40 5075深刻理解IdentityHashMap:http://dona ... -
深刻理解IdentityHashMap
2016-09-22 16:34 2365新建POJO package test; public ...
相关推荐
### 正确理解ThreadLocal:深入解析其工作原理与应用场景 #### 一、ThreadLocal的基本概念 `ThreadLocal`是Java平台提供的一种线程局部变量的解决方案,它为每一个使用该变量的线程都提供了独立的变量副本,使得每...
5. **源码解析** 要深入理解ThreadLocal的工作原理,需要查看其源码。ThreadLocal内部使用了一个ThreadLocalMap,它是一个基于ThreadLocal实例作为键,值为用户存储对象的弱引用表。每个线程都有一个这样的...
- `HTTP中Get与Post的区别.doc`: 深入解析HTTP协议中的GET和POST方法的差异,可能涵盖了请求方式、数据传输、URL长度限制、缓存策略等方面。 - `用HttpClient来模拟浏览器GET_POST.doc`: 展示了如何利用HttpClient库...
**ThreadLocal源代码解析** `ThreadLocal`的`get`和`set`方法实际上依赖于`Thread`类中的一个`ThreadLocalMap`。当我们调用`set`方法时,`ThreadLocal`会将值存储在当前线程的`ThreadLocalMap`中,键为`ThreadLocal`...
threadlocal源码解析
Java源码解析ThreadLocal及使用场景 ThreadLocal是Java中一个非常重要的类,它在多线程环境下经常使用,用于提供线程本地变量。这些变量使每个线程都有自己的一份拷贝,使得多个线程可以独立地使用变量,不会彼此...
ThreadLocal类的作用:为每个线程创建独立的副本,从而保证了线程安全。 ThreadLocal使用代码示例: public class MyThreadLocalTest { private ThreadLocal threadLocal=new ThreadLocal(){ @Override protected...
本文将深入解析这两个概念以及它们在Android环境下的工作原理。 **ThreadLocal** 是一个线程局部变量,它为每个线程提供独立的副本。这意味着每个线程都拥有ThreadLocal变量的私有实例,它们之间互不干扰。在...
#### 三、ThreadLocal实现解析 `ThreadLocal`类内部维护了一个`ThreadLocalMap`结构,该结构存储了线程与线程局部变量之间的映射关系。每当一个新的线程创建并首次访问某个`ThreadLocal`实例时,都会在该线程的`...
#### 重要概念解析:ThreadLocal在Java多线程中的角色 ThreadLocal是Java语言中处理多线程编程中线程安全问题的一种有效策略,它首次出现在JDK1.2版本中。与传统的使用`synchronized`关键字或`Lock`接口来实现线程...
4. **示例代码解析**: - 示例代码展示了如何使用ThreadLocal来创建线程唯一的标识符。每个线程首次调用`UniqueThreadIdGenerator.getCurrentThreadId()`时,会分配一个唯一的ID,并将其保存在ThreadLocal中。之后...
《InheritableThreadLocal & ThreadLocal 深度解析》 在Java编程中,线程局部变量(ThreadLocal)和可继承线程局部变量(InheritableThreadLocal)是两种非常重要的工具,它们允许我们在多线程环境中创建独立于线程...
8. **面试题解析** - 在面试中,可能会遇到关于 `ThreadLocal` 生命周期管理、内存泄漏、与 `synchronized` 的比较以及在实际应用中的场景分析等问题。 通过以上介绍,我们可以了解到 `ThreadLocal` 在处理多线程...
スレッド不安全的解析 在我们的示例代码中,我们可以看到一个典型的线程不安全的场景。在DateUtil类中,我们使用了synchronized关键字来同步parseymdhms()方法,以避免线程安全问题。然而,这种解决方案是非常不...
下面我们将详细解析这两个方法的实现过程。 当我们在代码中调用`ThreadLocal.set(T value)`时,实际上发生了以下步骤: 1. 首先,获取当前运行的线程对象,即`Thread.currentThread()`。 2. 接着,调用`values()`...
《ThreadPoolExecutor源码解析》 ThreadPoolExecutor是Java并发编程中重要的组件,它是ExecutorService接口的实现,用于管理和调度线程的执行。理解其源码有助于我们更好地控制并发环境下的任务执行,提高系统的...
例如,`SimpleDateFormat` 是非线程安全的,因为它内部使用了一个`Calendar`对象,多个线程同时使用一个`SimpleDateFormat`实例进行解析或格式化日期时,可能会出现线程A操作未完成而线程B已经开始的问题,导致数据...
// 获取当前业务需要的数据源key,例如从ThreadLocal中获取 } } ``` 最后,在`@EnableTransactionManagement`注解的配置类中,指定我们的动态数据源: ```java @Configuration @EnableTransactionManagement ...
### 苍穹外卖系统中的ThreadLocal应用:共享当前登录用户ID #### 一、背景介绍 在现代Web应用开发中,特别是在微服务架构下,如何有效地管理用户会话信息成为了一个重要的问题。对于苍穹外卖这类应用而言,保证每...