我们知道,在多线程访问同一个共享变量的情况下,为了保证不出现并发问题,我们往往需要对其进行同步操作,一般是通过加锁。那么,有没有一种方法,创建一个变量,使得每个线程对其进行访问都是访问自己线程的变量呢?ThreadLocal就可以实现这个操作。
何为ThreadLocal
ThreadLocal是JDK提供的,位于java.lang包中,它提供了一个线程的本地变量,当你创建了一个ThreadLocal变量,多个线程访问的都是这个变量的本地副本,对其进行操作实际上也是操作自己本地内存里面的变量,从而避免了线程安全问题。在介绍源码原理前,我们先来看一段使用ThreadLocal的代码加深理解。
public class ThreadLoaclTest {
//ThreadLocal变量
static ThreadLocal<String> localVariable = new ThreadLocal<>();
public static void main(String[] args) {
//创建线程t1,将ThreadLocal类型的localVariable变量赋值为t1-localVariable。
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
localVariable.set("t1-localVariable");
print("t1");
}
});
//创建线程t2,将ThreadLocal类型的localVariable变量赋值为t2-localVariable。
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
localVariable.set("t2-localVariable");
print("t2");
}
});
//启动线程t1,t2
t1.start();
t2.start();
}
//print函数,打印当前线程本地内存中localVariable变量的值
public static void print(String str) {
System.out.println(str + ":" + localVariable.get());
}
}
运行结果如下。
t2:t2-localVariable
t1:t1-localVariable
在上面这段代码中,我们在线程t1,t2中分别用set( )方法对ThreadLocal类型的localVariable变量进行赋值并将其打印,结果发现通过get( )函数获取的值是线程本地内存中的值。
ThreadLocal原理
Thread类中有一个threadLocals和一个inheritableThreadLocals变量,他们都是ThreadLocalMap类型的变量,而ThreadLocalMap是一个定制化的HashMap,它存储的是key-value键值对。
默认情况下线程中的这两个变量的都是null,只有当前线程第一次调用ThreadLocal的set( )方法或者get( )方法时才会去创建他们。其实每个线程的本地变量不是放在ThreadLocal实例中,而是放在调用线程的threadLocal变量中,也就是说,ThreadLocal类型的变量会放在当前线程的内存空间中。ThreadLocal就是一个工具壳,通过调用它的set( )将value值放入调用线程的threadlocals变量中存放起来,通过get( )将调用线程中的threadLocals值取出。
第一次读这段话的时候你可能半知半解,下面看一下下面的源码来理解。
1、ThreadLocal的 void set()
public void set(T value) {
//1、获取当前线程
Thread t = Thread.currentThread();
//2、将当前线程作为key去查找对应的线程变量,找到则设置。
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
//若为第一次调用则创建当前线程对应的threadLocals
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
2、ThreadLocal的 T get( )
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的threadLocals变量,它是ThreadLocalMap类型的
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的成员变量,该变量的类型为HashMap,其中key为我们定义的ThreadLocal变量的this引用,value则为我们使用set方法设置的值。我们通过ThreadLocal这个“外壳”对其进行操作,每个线程的本地变量我们可以存放在线程自己的threadLocals中,如果当前线程不消亡,这些本地变量就会一直存在,我们可以使用ThreadLocal的remove( )方法删除。
以上即是我对ThreadLocal的理解,是在学习了《Java并发编程之美》之后所作的学习笔记,本书内容充实且易懂,十分值得一读。