一、简述
ThreadLocal可以看作是一种数据结构,底层用map来实现。
ThreadLocal为每一个线程创建一个线程本地(thread-local)的变量,每个线程都有其独有的本地变量,相互之间的操作互不影响。
在多个线程并发访问同一个变量时,为了保证数据安全,synchronized或是Lock对对象上锁,当线程持有锁,其他线程不得访问,只有当持有锁的线程执行完毕,其他线程才能够获取锁继续执行。这种线程持有锁的机制,可以看作是花费额外时间来保证并发安全。
而使用ThreadLocal,将多个线程要访问的数据放入ThreadLocal中,则多个线程在访问ThreadLocal时,会为每一个线程创建一个 ”数据副本“,每个线程只是在操作自己的数据副本,相互之间不会影响。ThreadLocal的这种机制可以看作是用额外的空间来达到数据安全的目的。
代码示例:
public class ThreadLocalDemo extends Thread {
private static int value=3;
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
@Override
public void run() {
threadLocal.set(value);
int temp = threadLocal.get();
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + " " + temp--);
}
}
public static void main(String[] args) throws InterruptedException {
ThreadLocalDemo threadLocaDemo1 = new ThreadLocalDemo();
ThreadLocalDemo threadLocaDemo2 = new ThreadLocalDemo();
ThreadLocalDemo threadLocaDemo3 = new ThreadLocalDemo();
threadLocaDemo1.start();
threadLocaDemo2.start();
threadLocaDemo3.start();
}
}
二、源码
ThreadLocal的源码并不复杂,在此尝试扒一下源码。
首先看ThreadLocal.java的Outline:
从方法名就能知道方法的作用是什么。另外还有一个protected方法initialValue(),可以用来返回current thread的initial value,当第一次调用get()会返回这个初始值,如果在get()之前没有set(),返回null。
在必要时可以覆写initialValue()来指定一个初始值,这样不调用set()直接get()也不会返回null:
ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
protected Integer initialValue(){
//指定初始值为0
return 0;
}
};
看一下set()的源码:
可以看到set()是用一个ThreadLocalMap类型的map来保存数据,以ThreadLocal对象为key,存储的数据为value。set()首先用getMap()来获取map,获取到map之后将value存入,如果map不存在新建map。
点进getMap()看一看:
getMap()返回的threadLocals是在Thread类中一个类型为ThreadLocalMap的属性。
点进createMap():
创建了一个map,将ThreadLocal对象作为key,传入的值作为value存入,并将map保存在Thread的threadLocals属性中。
再看一下get()的源码:
get()获取Thread的map,如果map不为空,继续获取Entry,再获取value;如果map为空的话,调用setInitialValue()方法,setInitialValue()是将initialValue存入map。
(ThredLocal、ThredLocalMap、Entry之间的类关系:)
-------------------------------------------------------summary---------------------------------------------------------
以上,可以明了ThreadLocal数据隔离的过程:ThreadLocal好像一个壳,存储数据的其实是ThreadLocalMap,而每一个线程(Thread)都有一个独立的ThreadLocalMap(threadLocals属性),Thread和ThreadLocal通过ThreadLocalMap关联起来,每个线程(Thread)在操作ThredLocal时,操作的都是他自己的ThreadLocalMap。