一. ThreadLocal是什么
ThreadLocal
是线程本地数据存储类,通过ThreadLocal
可以在特定的线程中存储数据和变量, 并且这些数据之后只能由该线程访问,其他线程是访问不了的, 保证各个线程里数据和变量的独立性; 即ThreadLocal
使每个线程可以访问自己内部的副本变量。
二. 通过例子来了解ThreadLocal
下面通过一个简单的例子来说明ThreadLocal
的作用以及使用;
代码如下:
public class ThreadLocalDemo {
private static ThreadLocal<String> mThreadLocalDesc = new ThreadLocal<>();
public static class Thread1 extends Thread {
@Override
public void run() {
super.run();
mThreadLocalDesc.set("this is thread-1");
System.out.println("Thread1 mThreadLocalDesc = "+mThreadLocalDesc.get());
}
}
public static class Thread2 extends Thread {
@Override
public void run() {
super.run();
mThreadLocalDesc.set("this is thread-2");
System.out.println("Thread2 mThreadLocalDesc = "+mThreadLocalDesc.get());
}
}
public static void main(String[] args) {
mThreadLocalDesc.set("this is main thread");
System.out.println("main mThreadLocalDesc = "+mThreadLocalDesc.get());
Thread1 thread1 = new Thread1();
Thread2 thread2 = new Thread2();
thread1.start();
thread2.start();
}
}
我们分别使用ThreadLocal
来在Thread1, Thread2 和 main线程这三个线程里设置相应的变量, 并且都打印出来,检验是否保存了线程各自变量的值, 程序运行结果如下:
三.源码解析ThreadLocal类
实现思想,每个线程维护一个
ThreadLocalMap
的映射表,映射表的key
是ThreadLocal
实例本身,value
是要存储的副本变量。ThreadLocal
实例本身并不存储值,它只是提供一个在当前线程中找到副本值的 key。
ThreadLocal 类定义如下:
public class ThreadLocal<T>
从类的声明可以看出ThreadLocal
是一个泛型类, 所以它可以用来存储任何类型的线程本地数据;
ThreadLocal类提供的几个方法:
public T get() { }
get 方法是用来获取 ThreadLocal 在当前线程中保存的变量副本
public void set(T value) { }
set 用来设置当前线程中变量的副本
public void remove() { }
remove 用来移除当前线程中变量的副本
protected T initialValue() { }
initialValue 是一个protected方法,一般是用来在使用时进行重写的,做初始化操作
1. 先分析set方法的实现:
public void set(T value) {
//取得当前线程
Thread t = Thread.currentThread();
//获取当前线程 ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
Thread类的threadLocals字段定义如下:
//与此线程相关的ThreadLocal值。这个映射由ThreadLocal类维护。
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap
来存储每个线程副本变量,它是ThreadLocal
里的一个静态内部类。ThreadLocalMap
也是采用的散列表(Hash)思想来实现的
ThreadLocalMap
的实现方式:
Map
是一种 key-value
形式的数据结构,ThreadLocalMap
使用 Entry
类来存储数据,下面是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
实例作为 key
,副本变量作为 value
存储起来, Entry
中对于 ThreadLocal
实例的引用是一个弱引用;
整个 set
方法就是先取得当前线程,然后通过 getMap(t) 方法获取到一个Map
,Map
的类型为ThreadLocalMap
, 如果此时获取到的map = null
,就是先创建ThreadLocalMap
, 否则就存储该变量, 注意这里获取键值对传进去的是 this,而不是当前线程t.
2. 再分析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();
}
get
方法也是先获取当前线程, 然后再获取ThreadLocalMap
, 通过ThreadLocalMap
的getEntry(this)
直接获取value;
如果在map
中查找不到对应的存储,则会通过调用setInitialValue方法返回初始化时的初始值,而默认情况下,initialValue方法返回的是null。