目 录:
-
深入理解ThreadLocal
-
ThreadLocal的基本用法
-
ThreadLocal源码分析
-
ThreadLocal使用场景
-
使用ThreadLocal应注意什么
一:深入理解ThreadLocal
ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
ThreadLocal 位于java.lang包下, 该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
二:ThreadLocal的基本用法
ThreadLocal包含4个基本方法: get()、set()、initialValue()、remove() ;
T | get()
返回此线程局部变量的当前线程副本中的值。 |
protected T | initialValue()
返回此线程局部变量的当前线程的“初始值”。 |
void | remove()
移除此线程局部变量当前线程的值。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。 |
void | set(T value)
将此线程局部变量的当前线程副本中的值设置为指定值。 |
三: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); //获取当前线程相关的ThreadLocalMap
-
if (map != null) {
-
ThreadLocalMap.Entry e = map.getEntry(this); //
-
if (e != null)
-
return (T)e.value;
-
}
-
return setInitialValue(); //如果获取不到当前线程相关ThreadLocalMap对象,则调用“初始值”方法。开发者可重写initialValue();
-
}
获取一个和当前线程相关的ThreadLocalMap
-
/**
-
* 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;
-
}
-
/**
-
* 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); //将该变量赋值到ThreadLocal对象。
-
else
-
createMap(t, value); //如果该ThreaLocal为空,则创建该对象并赋值该变量
-
}
-
创建ThreadLocal对象
-
/**
-
* 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
-
* @param map the map to store.
-
*/
-
void createMap(Thread t, T firstValue) {
-
t.threadLocals = new ThreadLocalMap(this, firstValue);
-
}
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;
-
}
-
-
// remove方法
-
-
public void remove()
{
ThreadLocalMap localThreadLocalMap = getMap(Thread.currentThread());
if (localThreadLocalMap != null)
localThreadLocalMap.remove(this);
}
四:ThreadLocal的使用场景
ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,就可以使用ThreadLocal。
五:使用ThreadLocal 应注意什么
1.使用ThreadLocal时,应先使用set方法对其"赋值"操作,如果未先进行赋值,会报空指针异常(如果未重写initialValue()方法)。或者重写initialValue()方法!
2.使用完ThreadLocal后,建议调用remove()通知GC回收该对象,勿直接将ThreadLocal置为null,否则可能导致OutOfMemoryError异常。
3.还有一点就是,ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象,而值就是你所设置的对象了。