--- JDK1.8 ---
一.什么是ThreadLocal?
它更确切的名字应该是“thread-local variable manager”——线程本地变量管理者。它用于实现线程间【数据隔离】,在线程中使用它存储的变量或数据,仅可在当前线程中访问使用,在其他线程中不可见。
其实,它仅仅是一个管理者,真正存储数据的不是它,而是一个叫做ThreadLocalMap的对象,每个线程都有一个自己的hreadLocalMap对象(在Thread中对象名为:threadLocals或者inheritableThreadLocals)。
二、结构原理示意图【划重点】
图中,请注意颜色的对应关系。在主线程中创建了3个线程,和3个不同类型的ThreadLocal对象。分别在3个线程中对K1、K2、K3对象读取和设置数据后,3个线程的ThreadLocalMap中的数据如上图所示。代码如下:
//主线程中
ThreadLocal<String> k1 = new ThreadLocal<>();
ThreadLocal<Integer> k2 = new ThreadLocal<>();
ThreadLocal<Object> k3 = new ThreadLocal<>();
k1.set("main thread");
k2.set(1000);
k3.set("hallo~");
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread1:"+k1.get());
System.out.println("thread1:"+k2.get());
System.out.println("thread1:"+k3.get());
k1.set("123");
k2.set(1);
k3.set("sss");
System.out.println("thread1:"+k1.get());
System.out.println("thread1:"+k2.get());
System.out.println("thread1:"+k3.get());
System.out.println();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread2:"+k1.get());
System.out.println("thread2:"+k2.get());
System.out.println("thread2:"+k3.get());
k1.set("abc");
k2.set(2);
k3.set(k3);
System.out.println("thread2:"+k1.get());
System.out.println("thread2:"+k2.get());
System.out.println("thread2:"+k3.get());
System.out.println();
}
});
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread3:"+k1.get());
System.out.println("thread3:"+k2.get());
System.out.println("thread3:"+k3.get());
k1.set("haha");
k2.set(10);
k3.set(new Object());
System.out.println("thread3:"+k1.get());
System.out.println("thread3:"+k2.get());
System.out.println("thread3:"+k3.get());
System.out.println();
}
});
thread1.start();
thread2.start();
thread3.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main:"+k1.get());
System.out.println("main:"+k2.get());
System.out.println("main:"+k3.get());
运行结果如下:
thread1:null
thread1:null
thread1:null
thread1:123
thread1:1
thread1:sss
thread2:null
thread2:null
thread2:null
thread2:abc
thread2:2
thread2:java.lang.ThreadLocal@fb096c4
thread3:null
thread3:null
thread3:null
thread3:haha
thread3:10
thread3:java.lang.Object@68119265
main:main thread
main:1000
main:hallo~
Process finished with exit code 0
三、相关源码
set方法:
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //【注意这里】
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY]; //【初始值为16】
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
get方法 :
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
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();
}
四、注意:
1、存取数据时,ThreadLocal先按当前Thread对象查找到对应的hreadLocalMap,这就是线程之间的根本安全保障。
2、每个线程都有一个自己的hreadLocalMap对象(初始值为null),hreadLocalMap实际上就是一个Entry数组,初始化大小为16个空间,如果存满则2倍扩容。
3、Entry中的K值(this)是当前ThreadLocal对象,V值是要存储的对象(数据)。所以一个ThreadLocal对象在一个线程中最多存一个对象(数据)。
4、一个ThreadLocal对象可以在多个Thread中使用。
5、如果考虑父子线程的继承问题,需要使用InheritableThreadLocal,这里暂不讨论。