Java并发编程-ThreadLocal
一、ThreadLocal 的理解
ThreadLocal 由 JDK 包提供,它提供了线程本地变量,如果创建了一个 ThreadLocal 变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。当多个线程操作这个变量的时候,实际上是操作自己本地内存里面的变量,从而避免了线程安全问题。创建了一个 ThreadLocal 变量后,每个线程都会复制一个变量到自己的本地内存中。如图所示:
二、ThreadLocal 的示例
代码如下:
public class ThreadLocalTest {
// 创建ThreadLocal变量
public static ThreadLocal<String> localVariable = new ThreadLocal<>();
// 打印方法
public static void print(String str) {
// 打印当前线程本地内存中localVariable变量的值
System.out.println(str + ":" + localVariable.get());
// 清除当前线程本地内存中localVariable变量的值
localVariable.remove();
}
public static void main(String[] args) {
// 创建线程thread1
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
// 设置线程thread1中本地变量localVariable的值
localVariable.set("thread1 local variable");
print("thread1");
System.out.println("thread1 remove after" + ":" + localVariable.get());
}
});
// 创建线程thread2
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
// 设置线程thread2中本地变量localVariable的值
localVariable.set("thread2 local variable");
print("thread2");
System.out.println("thread2 remove after" + ":" + localVariable.get());
}
});
// 启动线程
thread1.start();
thread2.start();
}
}
运行结果如下:
thread1:thread1 local variable
thread2:thread2 local variable
thread1 remove after:null
thread2 remove after:null
将 print() 方法中的 localVariable.remove() 注释,再次运行得到运行结果:
thread1:thread1 local variable
thread2:thread2 local variable
thread1 remove after:thread1 local variable
thread2 remove after:thread2 local variable
三、ThreadLocal 的原理
ThreadLocal 类提供的几种方法:
public void set(T value) { }
public T get() { }
public void remove() { }
1、public void set(T value) { }
public void set(T value) {
// (1)获取当前线程
Thread t = Thread.currentThread();
// (2)将当前线程作为key,查找对应的线程变量,如果找到则设置
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
// (3)如果第一次调用则创建当前线程对应的HashMap
createMap(t, value);
}
代码(1)首先获取当前的使用线程,然后使用其作为参数传入 getMap(t) 方法,getMap(Thread t) 的代码如下:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
可以看出,getMap(t) 的目的是获取当前线程自己的 threadLocals 变量。
如果 getMap(t) 的返回值不为空,则把 value 值设置到 threadLocals 中,也就是把当前变量值放入当前线程的内存变量 threadLocals 中。其中 this 代表 ThreadLocal 的实例对象引用,value 是通过 set 方法传递的值。
如果 getMap(t) 的返回值为空,则说明是第一次调用 set 方法,这时候会创建当前线程的 threadLocals 变量。createMap(t, value):
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
可以看出,createMap(t, value) 方法创建当前线程的 threadLocals 变量。
2、T get() { }
public T get() {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取当前线程的threadLocals变量
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();
}
首先获取当前线程实例,如果当前线程的 threadLocals 变量不为 null,则直接返回当前线程绑定的本地变量,否则进行初始化。
3、void remove() { }
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
如果当前线程的 threadLocals 变量不为空,则删除当前线程中指定 ThreadLocal 实例的本地变量。
总结:每个线程内部都有一个名为 threadLocals 的成员变量,该变量的类型 HashMap,其中 key 为我们定义的 ThreadLocal 变量的 this 引用,value 则为我们使用 set 方法设置的值。每个线程的本地变量存放在线程自己的内存变量 threadLocals 中,如果当前线程一直不消亡,则这些本地变量会一直存在,所以可能会造成内存溢出,因此使用完毕后需要调用 ThreadLocal 的 remove 方法删除对应线程的 threadLocals 中的本地变量。如图所示: