一、ThreadLocal的理解
1、ThreadLocal即线程本地变量或者说是线程本地存储,ThreadLocal为中的变量在每个线程中都创建一个副本,那么每个线程可以访问自己内部的副本变量。
2、因为ThreadLocal为每个线程中的变量都创建了一个副本,即每个线程中都会有该变量,且线程内部任何地方都可以使用,线程之间互不影响,这样就不会存在线程安全。
但是,使用ThreadLocal会产生大量的副本,所以会比不使用ThreadLocal占用更多的内存资源。
二、深入理解ThreadLocal类
1、了解ThreadLocal的方法
(1) public T get(){}
(2) public void set(T value){}
(3) public void remove(){}
(4) protected T initialValue() {}
get()方法是获取变量副本,set(T value)是设置变量副本,remove()是移除变量副本,initialValue()是protected方法,一般在使用时用来重写。
2、ThreadLocal是如何创建变量副本的
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取ThreadLocalMap类型的map,t为当前线程
ThreadLocalMap map = getMap(t);
//获取key,value键值对(参数是this,而不是当前线程)
//如果获取成功,则返回value值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果获取不成功,返回setInitialValue();
return setInitialValue();
}
//getMap(Thread t)方法
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
在getMap(Thread t)方法中,返回的是一个线程的成员变量。
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
首先:在每个Thread内部会有一个ThreadLocal.ThreadLocalMap的成员变量threadLocals,threadLocals就是用来存储每一个变量副本的,键为当前的ThreadLocal变量,value为每一个变量副本(T类型);
初始时,ThreadLocals为空,当通过get(),set()方法就会对ThreadLocals进行初始化,并且以当前ThreadLocal为key值,以ThreadLocal要保存的变量副本为value,存到ThreadLocals中。
然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
下面为通过ThreadLocal创建的变量副本的例子
public class TestThreadLocal {
ThreadLocal<Long> longThreadLocal = new ThreadLocal<>();
ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
public void set(){
longThreadLocal.set(Thread.currentThread().getId());
stringThreadLocal.set(Thread.currentThread().getName());
}
public Long getLongThreadLocal() {
return longThreadLocal.get();
}
public void setLongThreadLocal(ThreadLocal<Long> longThreadLocal) {
this.longThreadLocal = longThreadLocal;
}
public String getStringThreadLocal() {
return stringThreadLocal.get();
}
public void setStringThreadLocal(ThreadLocal<String> stringThreadLocal) {
this.stringThreadLocal = stringThreadLocal;
}
public static void main(String[] args) throws InterruptedException {
TestThreadLocal threadLocal = new TestThreadLocal();
threadLocal.set();
System.out.println(threadLocal.getLongThreadLocal());
System.out.println(threadLocal.getStringThreadLocal());
Thread thread = new Thread(){
@Override
public void run() {
threadLocal.set();
System.out.println(threadLocal.getLongThreadLocal());
System.out.println(threadLocal.getStringThreadLocal());
}
};
thread.start();
thread.join();
System.out.println(threadLocal.getLongThreadLocal());
System.out.println(threadLocal.getStringThreadLocal());
}
}
输出结果:
三、应用场景
最常见的ThreadLocal使用场景为 用来解决 数据库连接、Session管理等。