ThreadLocal是一个用于存储多线程变量的类,它可以把线程与设置的值对应起来,因为它为变量在每个线程都创建了一个副本。访问的时候每个线程只能访问到自己的副本变量。
看如下代码:
public class Main {
public static void main(String[] args) throws InterruptedException {
ThreadLocal threadLocal = new ThreadLocal();
Thread1 thread1 = new Thread1("线程A",threadLocal);
Thread1 thread2 = new Thread1("线程B",threadLocal);
Thread1 thread3 = new Thread1("线程C",threadLocal);
Thread1 thread4 = new Thread1("线程D",threadLocal);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
class Thread1 extends Thread{
ThreadLocal threadLocal;
public Thread1(String name,ThreadLocal threadLocal){
super(name);
this.threadLocal = threadLocal;
}
@Override
public void run() {
try {
for(int i = 0;i< 10;i++){
threadLocal.set(getName()+i);
System.out.println(getName()+": "+threadLocal.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出为:
线程B: 线程B0
线程D: 线程D0
线程A: 线程A0
线程C: 线程C0
线程C: 线程C1
线程A: 线程A1
线程B: 线程B1
线程D: 线程D1
线程D: 线程D2
线程C: 线程C2
线程A: 线程A2
线程B: 线程B2
线程B: 线程B3
线程D: 线程D3
线程A: 线程A3
线程C: 线程C3
线程C: 线程C4
线程A: 线程A4
线程B: 线程B4
线程D: 线程D4
可以看到效果和预期是一样的,每个线程都只是获取自己设置的值。
每个线程内部存储着一个ThreadLocalMap 实例的,ThreadLocalMap 实例存储着一个或者多个ThreadLocal实例,获取的时候就可以根据当前线程获得对应设置的值
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);
}
//获取Map进行设值,如果没有就新建一个map
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; //Thread的一个属性,初始值为NUll;
}
//这个是Thread的代码,每个Thread存储一个map的
ThreadLocal.ThreadLocalMap threadLocals = null;
//新建一个Map,ThreadLocalMap是内部类
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue); //设置值
}
//ThreadLocalMap的构造方法
ThreadLocalMap(ThreadLocal> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
可以看出这里是把每个线程的threadLocals 设置为一个ThreadLocalMap 的实例,ThreadLocalMap 存储的是key是threadLocals实例,value是对应的值
如下图:
每个线程都有一个threadLocals ,指向对应的ThreadLocalMap 实例,ThreadLocalMap 是一个map结构,ThreadLocalMap 里面每个Entry存储着一个对应的value值,Entry实例的key是ThreadLocal实例,value是当前线程在这个ThreadLocal设置的value值;如果对map结果不熟,可参考Java从入门到放弃(十)集合框架之HashMap源码(1);
ThreadLocalMap的Hash,ThreadLocalMap的key值取的是ThreadLocal实例,这里hash和hashmap有一些不同:
private final int threadLocalHashCode = nextHashCode(); //hash值
private static AtomicInteger nextHashCode =
new AtomicInteger(); //原子操作类
}
private static final int HASH_INCREMENT = 0x61c88647; //每次hash间隔
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT); //获取下一个hash值
}
这里是把hash值按照HASH_INCREMENT 的间隔相加。这样子可以保证不会出现相同hash的情况,避免Entry数组的Entry太过于集中,让Entry均匀分布。(这个数字可以让计算出来的索引比较均匀分布)
get方法
public T get() {
Thread t = Thread.currentThread(); //获取当前线程
ThreadLocalMap map = getMap(t); //获取map
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); //获取对应Entry
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue(); //没有就返回初始化的值
}
//设置初始化的值
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
//初始化
protected T initialValue() {
return null;
}
这里就是获取当前线程,取得线程的map,根据Threadlocal为key找到对应的value值。如果没有设置就初始化并且设置对应值为null;
remove方法
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
这个也很简单,就是获取对应的map然后删除。