ThreadLocal用于在线程中保存数据,在其中保存的数据仅属于当前线程,其他线程是无法访问到的。
ThreadLocal用于在同一个线程间的不同类和方法之间共享数据的场景,也可用于不同线程间隔离数据的场景。
ThreadLocal 利用Thread中的ThreadLocalMap进行数据存储。
public class Demo01 {
public static ThreadLocal<String> threadLocal=new ThreadLocal<String>();
public static void main(String[] args) throws InterruptedException {
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
threadLocal.set("zzz");
show();
Sample.dosth();
System.out.println("thread1");
}
},"线程一");
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
threadLocal.set("xxxx");
show();
Sample.dosth();
System.out.println("thread2");
}
},"线程2");
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("THE END!");
}
public static void show() {
System.out.println("show:"+Thread.currentThread().getName()+threadLocal.get());
}
}
class Sample {
public static void dosth() {
System.out.println("dosth:"+Thread.currentThread().getName()+Demo01.threadLocal.get());
}
}
thread1 和thread2是两个不同的线程,thread1中存入局部变量zzz,thread2中存入局部变量xxxx,在调用show()方法获取局部变量时,thread1获取的是thread存入的局部变量,thread2火区的是thread2存入的局部变量,但是他们使用的是同一个threadLocal对象,可见,局部变量是存在该线程中的。
那么ThreadLocal是怎么向线程中存放数据和获取数据的呢?
向线程中存入数据
public void set(T value) {
Thread t = Thread.currentThread();
//获取当前线程对应的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//如果当前map为空,则key 为当前的threadlocal
map.set(this, value);
else
//如果map为空则创建map
createMap(t, value);
}
从线程中获取数据
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//根据threadlocal获取值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
从线程的threadkocal中删除数据
ublic void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
//如果threadlocalMap不为空,删除
if (m != null)
m.remove(this);
}
ThreadLocalMap的内部结构
ThreadLocalMap内部是一个Entry类型的数组,每个Entry的key为ThreadLocal对象,value为存储的数据。
ThreadLocalMap使用ThreadLocal做key的原因
如果一个线程只是用一个ThreadLocal对象,那么使用Thread做key也是可以的,但是在实际情况中,一个Thread会有多个ThreadLocal对象,就可能会造成如下情况:
一个Thread里存了多个ThreadLocald的值,在根据thread取就会发生混乱。所以我们采用ThreadLocal做为键。
ThreadLocalMap底层是使用Entry数组保存简直对数值的,当我们调用get()方法时会使用一个下标来进行数组中元素的访问。
那么,该下标从何而来呢?
实际上是通过key的哈希值与数组长度减一做“&按位与”运算得到的。