ThreadLocal是什么
ThreadLocal是一个关于创建线程局部变量的类。
通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。
本文需要解决一下几个问题
-
每个线程的变量副本是存储在哪里的?
-
变量副本是怎么从共享的那个变量赋值出来的?源码中的threadlocal的初始值是什么时机设置的?
-
ThreadLocal是如何实现了多个线程之间每个线程一个变量副本的?它是如何实现共享变量的。
ThreadLocal机制主要由Entry、ThreadLocalMap、Thread、ThreadLocal这四个类相互协作实现的。
我们来看set()方法
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,去获取实体map,并set,如果没有则Creat
我们再来看get()方法
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();
}
同set方法差不多
我们来看看**ThreadLocalMap **
//初始化大小
private static final int INITIAL_CAPACITY = 16;
//容器为数组
private Entry[] table;
Entry
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry的定义很简单,它扩展自ThreadLocal类型的WeakReference类,是一个key-value对类。key是ThreadLocal对象的弱引用,value是线程的内部变量。
Entry使用弱引用作为key目的是,希望在外部不再需要访问ThreadLocal对象时可以让GC尽快地回收对象,而不必等到线程结束后。
当GC回收ThreadLocal对象后,再通过Entry.get()获取ThreadLocal对象时返回null,这使得内部能够感知什么时候不需要再持有对value的引用,从而释放Entry对象的引用,进而释放value的引用,这时如果value在外部没有任何引用的话(通常你不应该在外部持有对value的引用),随后被GC回收。这种感知和释放的行为发生在ThreadLocal的get、set、remove操作时。
通常在Java的世界里,我们不需要关系对象的释放,大部分情况下GC会自动帮我们回收。
但是如果使用ThreadLocal不当,是有可能导致内存泄漏的。
ThreadLocal释放内部变量通常在以下时机:
线程结束后
显式调用remove
在调用get、set时,如果探测到ThreadLocal对象的弱引用对象get返回null顺便释放。
所以,如果线程存活的生命周期很长,特别是和进程一样长的话,就要特别注意防止ThreadLocal引入内存泄漏的风险,在不需要再使用某个线程内部变量时记得显式调用remove清理掉。
网友评论