ThreadLocal是什么?有哪些应用场景?
大家好,我是joker,希望你快乐。
多线程情况下操作共享变量会产生线程安全问题,需要进行线程间同步,但是并不是所有的情况都是多线程去操作共享变量,有些线程是无状态的只进行操作处理,不涉及共享数据操作,所以就需要threadlocal登场了。
threadlocal是什么?
threadlocal根据命名来看,分为两部分:thread,local,线程,本地的,私有的。通过源码中的注释This class provides thread-local variables
可以看出threadlocal这个类提供线程本地变量,作为一个线程内的全局变量。
threadlocal源码分析
下面通过代码进行分析,先来看一个类图:
通过类图可以看出,ThreadLocal依赖于ThreadLocalMap,Thread通过threadLocals属性与ThreadLocalMap组成1:1的组合关系。
Thread的私有数据存储在ThreadLocalMap内。
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);
}
}
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();
}
Thread中的threadLocals属性:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap结构比较多,只取了一部分:
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;
}
}
/**
* 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;
可以看到ThreadLocalMap中维护了一个Entry类型数组,用于存储最终的数据,可以先把ThreadLocalMap认为是一个hashmap,还是有很多不同,下次再做比较。
简单做一个总结,Thread的私有变量是存储在自身上的,类型为一个ThreadLocal.ThreadLocalMap的变量,ThreadLocal是一个工具类,用于去维护线程的本地变量。
threadlocal的应用场景
通过上面的分析,可以看出ThreadLocal是一个线程安全,线程私有的,线程生命周期内的全局变量。
通过这些优势特点,可以看出ThreadLocal是线程安全的,避免某些情况需要考虑线程安全进行同步带来的性能损失。
线程生命周期内的全局变量,可以作为一个上下文信息,在整个线程的运行周期内共享数据。
threadlocal可能产生的问题,如何避免
map的key为ThreadLocal实例,key使用了弱引用,所以当把ThreadLocal实例设置为null后,没有任何强引用指向原来的ThreadLocal实例,所以ThreadLocal实例就可以顺利被GC回收。
仍然会造成内存泄漏问题,虽然key为弱引用类型,ThreadLocal能被及时回收,但是value却依然存在内存泄漏问题。ThreadLocal实例的引用设置为null后,没有任何强引用指向原来的ThreadLocal实例,ThreadLocal实例就可以被GC回收,ThreadLocal实例被回收后,value永远不会被访问到,所以存在内存泄漏问题。threadLocals对象中的Entry对象不再使用后,如果没及时清除Entry对象,而程序自身也无法通过垃圾回收机制自动清除,就可能导致内存泄漏。
- 如何解决
1. 每次用完ThreadLocal都记得调用remove()方法清除数据
2. 将ThreadLocal变量尽可能的定义成static final
- 内部优化:
1. 调用set()方法时,会采样清理,全量清理,扩容时还会继续检查
2. 调用get()方法时,没有直接命中,向后唤醒查找时会进行清理
3. 调用remove()方法时,除了清理当前Entry,还会向后继续清理
来源:http://www.cnblogs.com/Crazy_Joker
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。