ThreadLocal详解
多个线程需要对一个共享变量写入时容易出现并发问题。
ThreadLocal可以做到:当多个线程对一个共享变量进行访问的时候,实际上访问的是本线程的本地变量。
一、ThreadLocal实现原理
Thread类中有threadlocals变量,threadlocals变量的类型是ThreadLocal.ThreadLocalMap,是在ThreadLocal类中实现的一个类似于Map的结构,ThreadLocalMap结构实际上是一个Entry对,key值是ThreadLocal对象,value值是ThreadLocal对象的值,如下图所示:

二、ThreadLocal实现细节
1、get实现
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的threadLocals变量
ThreadLocalMap map = getMap(t);
//如果当前线程的threadLocals变量不为空,则返回对应的value值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果当前线程threadLocals变量为null,则初始化当前线程的threadLocals变量
return setInitialValue();
}
private T setInitialValue() {
//定义value = null
T value = initialValue();
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的threadLocals变量
ThreadLocalMap map = getMap(t);
//如果当前线程的threadLocals变量不为空,则将当前的threadLocals变量的value值置为null
if (map != null)
map.set(this, value);
//如果当前线程的threadLocals变量为空,则初始化创建一个当前线程的threadLocals变量,并将value置为null
else
createMap(t, value);
return value;
}
2、set实现
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的threadLocals变量
ThreadLocalMap map = getMap(t);
//如果当前线程的threadLocals变量不为空,则将该变量的value值设置为传入的value值
if (map != null)
map.set(this, value);
//如果当前线程的threadLocals变量为空,则初始化创建一个当前线程的threadLocals变量,并将该变量的value值设置为传入的value值
else
createMap(t, value);
}
3、remove实现
public void remove() {
//获取当前线程的threadLocals变量
ThreadLocalMap m = getMap(Thread.currentThread());
//如果不为空,则删除当前线程中指定ThreadLocal实例的本地变量
if (m != null)
m.remove(this);
}
三、内存泄露
我们先看看ThreadLocal态类里面的成员:
public class ThreadLocal<T>{
//......
static class ThreadLocalMap{
static class Entry extends WeakReference<ThreadLocal<?>>{
Object value;
Entry(ThreadLocal<?> k, Object v){
super(k);
value = v;
}
}
//......
}
//......
}
当一个线程调用ThreadLocal类的set方法设置当前线程的本地变量时,当前线程的ThreadLocalMap中会有一条记录,该记录的key值为ThreadLocal的弱引用,value为set方法设置的值。
如果当前线程一直存在,且没有调用ThreadLocal的remove方法,且这时候还有其他地方还有对ThreadLocal的引用,则当前线程的ThreadLocalMap变量里会同时存在对ThreadLocal变量的引用和对value对象的引用,它们不会被释放,因此会造成内存泄露。
由于线程的ThreadLocalMap中的key是弱引用,所以当前线程的ThreadLocalMap中的ThreadLocal变量的弱引用会在下一次gc时被回收,但是对应的value还是会造成内存泄露,这时候ThreadLocalMap中会存在key为null但是value不为null的Entry项。
解决方法:在每次使用完毕后调用remove方法。
四、线程安全
那么 ThreadLocal到底是不是线程安全的呢?NONONO!想要拥有线程安全,Value必须是不可变对象。
浙公网安备 33010602011771号