在并发编程中时常有这样一种需求:每条线程都需要存取一个同名变量,但每条线程中该变量的值均不相同。把对象或资源封闭在当前所在线程里,这样便成为了该线程的内部变量。
基本上,ThreadLocal对外提供的方法只有三个:
set(obj):向当前线程中存储数据
get():获取当前线程中的数据
remove():删除当前线程中的数据
最初碰到是在一个FTP服务器的小项目里,需要将share中的user用户信息存入每个用户新开的线程中,所以肯定要把线程和用户信息严格对应起来,如下图
下面举一个例子,看看怎么使用ThreadLocal:
public class ThreadLocalDemo {
public static void main(String[] args) throws InterruptedException {
final ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("main-thread : Hello");
Thread thread = new Thread(() -> {
// 获取不到主线程设置的值,所以为null
System.out.println(threadLocal.get());
threadLocal.set("sub-thread : World");
System.out.println(threadLocal.get());
});
// 启动子线程
thread.start();
// 让子线程先执行完成,再继续执行主线
thread.join();
// 获取到的是主线程设置的值,而不是子线程设置的
System.out.println(threadLocal.get());
threadLocal.remove();
System.out.println(threadLocal.get());
}
}
运行结果:
null
sub-thread : World
main-thread : Hello
null
我们看一下ThreadLocal的set方法源码,通过获取当前线程,ThreadLocalMap是ThreadLocal的一个静态内部类
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap中还有一个内部类Entry ,这是Entry源码:
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
再看看Thread中成员变量threadLocals的源码:
ThreadLocal.ThreadLocalMap threadLocals = null;
可见ThreadLocal并不维护ThreadLocalMap,它并不是一个存储数据的容器,它只是相当于一个工具包,提供了操作该容器的方法,如get、set、remove等。
而ThreadLocal内部类ThreadLocalMap才是存储数据的容器,并且该容器由Thread维护。每一个Thread对象均含有一个ThreadLocalMap类型的成员变量threadLocals,它存储本线程中所有ThreadLocal对象及其对应的值。
ThreadLocalMap由一个个Entry对象构成,一个Entry由ThreadLocal对象和Object构成。
恍然大悟!