1. ThreadLocal定义
ThreadLocal
是线程变量,用于填充当前线程的变量,该变量与其他线程是隔离的。
ThreadLocal使用场景
- 线程间数据隔离
- 进行事务操作,用于存储事务信息
- 数据库连接,
Session
会话管理 - 在进行对象跨层传递的时候,使用
ThreadLocal
可打破层次间的约束
2. 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);
}
先获取当前线程 t,以当前线程作为key
去ThreadLoaclMap
中查询该线程的ThreadLocalMap
,如果map
存在,则将当前线程作为key
,要存储的对象作为value
存进去,如果不存在,则创建一个,再保存。
那么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.
*/
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;
}
}
// 下面还有很多
}
ThreadLocalMap
是ThreadLoacl
中定义的一个类部类,里面定义了Entry
,使用当前线程作为key
,保存线程中的成员对象。
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();
}
获取当前线程 t,然后以 t 为key
,从ThreadLocalMap
里面取变量值。如果 值为 null
,则自动填充一个。
自动填充value :
/**
* 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;
}
/**
* Returns the current thread's "initial value" for this
* thread-local variable. This method will be invoked the first
* time a thread accesses the variable with the {@link #get}
* method, unless the thread previously invoked the {@link #set}
* method, in which case the {@code initialValue} method will not
* be invoked for the thread. Normally, this method is invoked at
* most once per thread, but it may be invoked again in case of
* subsequent invocations of {@link #remove} followed by {@link #get}.
*
* <p>This implementation simply returns {@code null}; if the
* programmer desires thread-local variables to have an initial
* value other than {@code null}, {@code ThreadLocal} must be
* subclassed, and this method overridden. Typically, an
* anonymous inner class will be used.
*
* @return the initial value for this thread-local
*/
protected T initialValue() {
return null;
}
其实填充的就是 null
。所以如果需要手动初始化的时候,如果需要默认值,则需重写initialValue()
方法。
remove()
方法源码:
/**
* 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);
}
从ThreadLocalMap
中移除。
内部的源码十分简单。
ThreadLocal
本身不存储值,每个ThreadLocal
中维护一个ThreadLocalMap
,以当前的ThreadLocal
作为key
来存储变量。
ThreadLocal
是一个弱引用,当为null时,会被当成垃圾回收。
重点:突然我们ThreadLocal
是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap
生命周期和Thread
的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap
的key
没了,但是value
还在,这就造成了内存泄漏。
解决办法:使用完ThreadLocal
后,执行remove
操作,避免出现内存溢出情况。
所以还是不知道何时使用?该如何使用?举个例子,当我们使用切面的时候,就可以用啦。比如利用自定义注解记录日志,可以将注解的参数放到当前的ThreadLoacl
中,记录日志的时候再取出。ThreadLocalMap
的value
用HashMap
保存参数。
网友评论