ThreadLocal
前不久逛博客看到了ThreadLocal类,然后就查阅了资料研究了一下。
ThreadLocal是一个类,它在其中定义了一个ThreadLocaMap内部类,这个内部类的特征使得每个线程在调用ThreadLocal.set(), ThreadLocal.get()方法时,保证存或者取得对象都是该线程自己的,从而保证了安全性。那他是如何做到的呢?
ThreadLocal.set()
先来看看threadLocal.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.
*
* @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);
}
复制代码
先调用getMap拿到ThreadLocalMap, 如果map不存在,则createMap,这里看到把this当作key存进去了,而this则是threadLocal对象本身.(其实是ThreadLocal的弱引用,很有可能导致内存泄露,但是threadLocalMap很好的解决了这个隐患,ThreadLocalMap.remove方法)。
/**
* 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;
}
复制代码
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
复制代码
ThreadLocal.get()
再看看threadLocal.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.
*
* @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();
}
复制代码
同样先去getMap拿到map,如果map存在,则通过this这个key拿到value,而在这个过程中,this对象是没有变过的,所以可以保证对象安全性的唯一性。
当然第一次调用get()方法时,map是为空的,此时会调用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;
}
protected T initialValue() {
return null;
}
复制代码
这里初始化了一个map,值为空。如果之前set()方法调用过了,这个方法就不会被调用。
ThreadLocalMap
ThreadLocalMap是定义在ThreadLocal中的内部类,是ThreadLocal发挥作用的关键。
/**
* 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++;
}
}
}
}
复制代码
但他真正的引用却是在Thread类中。
/* ThreadLocal values pertaining to this thread. This map is maintained by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
复制代码
在ThreadLocal.get()方法中可以看到获取的数据类型是Entry:
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
复制代码
ThreadLocalMap中用于存储数据的entry定义:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
复制代码
这边也可以看出存入的key的类型是ThreadLocal。
总结
到这里,我们就可以理解ThreadLocal究竟是如何工作的了
1.Thread类中有一个成员变量属于ThreadLocalMap类(一个定义在ThreadLocal类中的内部类),它是一个Map,他的key是ThreadLocal实例对象。
2.当为ThreadLocal类的对象set值时,首先获得当前线程的ThreadLocalMap类,然后以ThreadLocal类的对象为key,设定value。get值时则类似。
3.ThreadLocal变量的活动范围为某线程,是该线程“专有的,独自霸占”的,对该变量的所有操作均由该线程完成!也就是说,ThreadLocal 不是用来解决共享对象的多线程访问的竞争问题的,因为ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。当线程终止后,这些值会作为垃圾回收。
举个栗子:
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
复制代码
参考文章:www.cnblogs.com/xzwblog/p/7…
www.cnblogs.com/since1499/p…