目录
1:ThreadLocal是什么
ThreadLocal是Thread类的一个成员变量,每次创建一个线程,就会有属于这个线程自己的成员变量ThreadLocal,不同的线程不会相互干扰。在多线程的环境下get、set都是隔离的,保证了线程安全。在线程的生命周期内可以减少方法之间的参数传递,使用ThreadLocal来从传递参数减少复杂度。
2:ThreadLocal用法
2.1:代码实例:
public class Test {
//类变量
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
Thread thread1= new Thread(new Runnable() {
@Override
public void run() {
Test.threadLocal.set("thread1变量");
remove("thread1");
System.out.println("thread1获取当前线程的:"+threadLocal.get());
}
},"线程1");
Thread thread2= new Thread(new Runnable() {
@Override
public void run() {
Test.threadLocal.set("thread2变量");
remove("thread2");
System.out.println("thread2获取当前线程的:"+threadLocal.get());
}
},"线程2");
thread1.start();
thread2.start();
}
public static void remove(String s) {
//打印当前线程中本地内存中本地变量的值
System.out.println(s + " :" + threadLocal.get());
//清除本地内存中的本地变量
threadLocal.remove();
}
}
输出结果:
2.2:源码分析
ThreadLocal的set()方法:
ThreadLocal的set()方法实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象弱引用,值Value是传递进来的对象
我们得知,当我们在线程中使用Threadloacl并且set值的时候,这是一个线程的成员变量,线程独有。
可看出ThreadLocalMap是ThreadLocal的内部静态类,而它的构成主要是用Entry来保存数据 ,而且还是继承的弱引用。在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value。详细内容要大家自己去跟。
//Thread类的成员变量,ThreadLocal类的内部类ThreadLocalMap
ThreadLocal.ThreadLocalMap threadLocals = null;
//ThreadLocal的set方法
public void set(T value) {
Thread t = Thread.currentThread();//获取线程
//获取线程中的属性 threadLocalMap ,如果threadLocalMap 不为空,
//则直接更新要保存的变量值,否则创建threadLocalMap,并赋值
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
//创建ThreadLocalMap,并且赋值
createMap(t, value);
}
}
//创建ThreadLocalMap,接受参数是当前线程和传递的值
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//ThreadLocalMap构造器
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//默认16个Entity数组
table = new Entry[INITIAL_CAPACITY];
//hash取模,存放在对应的位置
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//key是threallocal entity是实现弱引用
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
//查看Entry 是继承了弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
//entity构造器
Entry(ThreadLocal<?> k, Object v) {
super(k);//key的构造函数是一个弱引用的ReferenceQueue
value = v;
}
}
ThreadLocal的get方法:
有源码可知就是获取当前线程的ThreadLocal的ThreadLocalMap中的entity
public T get() {
//1、获取当前线程
Thread t = Thread.currentThread();
//2:根据当前线程,获取对应的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
//3:获取threalLocalMap中存储的值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果ThreadLocalMap是null,初始化创建一个ThreadLocalMap
return setInitialValue();
}
ThreadLocal的remove方法:
为什么移除了key还要删除呢?实际上 ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,弱引用的特点是,如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉。
public void remove() {
//根据当前线程获取当前线程的ThreadLocalMap
ThreadLocalMap m = getMap(Thread.currentThread());
//移除threadlocal
if (m != null) {
m.remove(this);
}
}
/**
* Remove the entry for key.
*/
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
//移除key成功
if (e.refersTo(key)) {
//把Entity删除,为什么呢?
e.clear();
expungeStaleEntry(i);
return;
}
}
}
3:ThreadLocal知识点
1:属于每一个线程Thread的私有变量,线程独有安全,可以在线程中传递参数
2:set之后必须调用remove方法,因为ThreadLocal的ThreadLocalMap是Entity,Entity的key是ThreadLocal的弱引用,所以如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候会被清理掉的,这样一来 ThreadLocalMap中使用这个 ThreadLocal 的 key 也会被清理掉。但是,value 是强引用,不会被清理,这样一来就会出现 key 为 null 的 value。