1.概述:ThreadLocal被称为线程区域变量,用于在线程中保存数据,在ThreadLocal里保存的数据仅属于当前线程,别的线程访问不到,实现了线程之间的数据隔离,在同一个线程的不同方法中,可以通过ThreadLocal传递数据,无需大量的形参进行传值。
2.ThreadLocal的常用方法:
set方法:
public void set(T value) {
//获取当前执行线程的对象
Thread t = Thread.currentThread();
//获取当前线程对象的ThreadLocalMap
ThreadLocalMap map = getMap(t);
//如果map不为空,以ThreadLocal为key,将value值保存在map中
//否则先创建出ThreadLocalMap集合并将值保存在map中
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
get方法:
public T get() {
//获取当前执行线程的对象
Thread t = Thread.currentThread();
//获取当前线程的ThreadLocalMap集合
ThreadLocalMap map = getMap(t);
//如果当前map不为空,以自身ThreadLocal对象为key,取出map中的值并返回
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
remove方法:
public void remove() {
//获取当前线程的ThreadMap集合对象
ThreadLocalMap m = getMap(Thread.currentThread());
//如果ThreadMap集合不为空,就删除以当前ThreadLocal对象为key的value值
if (m != null)
m.remove(this);
}
在以上的三个方法中,我们可以看出通过ThreadLocal对象保存的值,都会保存在当前线程对象的ThreadLocalMap集合中,并且以ThreadLocal自身为key保存在集合中, 无论是存值还是取值都是以ThreadLocal自身为key操作ThreadLocalMap集合的,接下来,让我们学习一下ThreadLocalMap到底是什么?
3.ThreadLocalMap的内部结构:
//Thread类中的代码
ThreadLocal.ThreadLocalMap threadLocals = null;
从上面的代码中看出,每个Thread对象(即每个线程),都会定义一个ThreadLocalMap的集合对象,也就是ThreadLocal对象将数据保存的位置。
ThreadLocalMap部分代码:
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private static final int INITIAL_CAPACITY = 16;
//存放值的数组
private Entry[] table;
//构造方法
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);
}
}
首先,ThreadLocalMap是ThreadLocal类中的一个内部类,类中定义了一个Entry类型的数组来保存值,Entry类为ThreadLocalMap中的内部类,通过构造方法可以看出ThreadLocalMap的key为ThreadLocal类型。
注意:
(1)虽然ThreadLocalMap是ThreadLocal类中的一个内部类,但ThreadLocalMap声明是在Thread类中的,属于Thread类的成员变量。
(2)ThreadLocalMap虽然也是键值对集合,但是它并没有实现Map集合,与HashMap不同,它的数据结构仅仅是一个Entry类型的数组。
(3)每一个Thread线程对象都会有一个ThreadLocalMap类型的成员变量,用来保存属于这个线程的数据,并以ThreadLocal对象为key。
结合图片理解一下:
---------------------------------------------------------------------------------------------------------------------------------
4.案例实现:
public class Demo01 {
public static ThreadLocal<String> roleLocal=new ThreadLocal<String>();
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
roleLocal.set("苏妲己");
show();
Sample.dosth();
}
},"线程1");
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
roleLocal.set("闻太师");
show();
Sample.dosth();
}
},"线程2");
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("执行结束!");
}
public static void show() {
System.out.println("show:"+Thread.currentThread().getName()+"角色分配为:"+roleLocal.get());
}
}
class Sample{
public static void dosth() {
System.out.println("dosth:"+Thread.currentThread().getName()+"角色为:"+Demo01.roleLocal.get());
}
}
在上面的代码中,我们首先创建出了一个ThreadLocal类型的roleLocal对象, 然后创建出两个不同的线程,在线程中先使用roleLocal分别保存不同的角色名称,然后调用show()方法通过roleLocal取出角色名称,再调用Sample的dosth()方法通过roleLocal取出角色名称。
运行结果:
从运行结果看出,两个线程分别保存自己的角色名称,实现了线程之间的数据隔离,在show()方法和Sample类的dosth()方法之间,ThreadLocal实现了同一个线程中不同方法之间的数据传递。
ThreadLocal的应用场景:
(1)实现线程之间的数据隔离(比如:案例中两个线程之间分别存入不同的角色名称)
(2)跨函数传值,例如,案例中show()方法和Sample类的dosth()方法之间的角色名称数据,避免了通过形参传递,降低了类或者方法之间的耦合度。
注意事项:
(1)一个ThreadLocal对象在一个线程中只能存放一个值,可以使用多个ThreadLocal对象在一个线程中存放多个值。
(2)使用完TheadLocal保存的数据后,应该在finally代码块中调用ThreadLocal的remove()方法,避免内存泄露。