JAVA并发编程(2):ThreadLocal的使用以及实现原理
1.使用
ThreadLocal是java中的线程本地变量,如果创建了一个ThreadLocal变量,那么多线程中,每个线程都会拥有一个该变量的副本,具体使用方法如下:
public class ThreadLocalTest { private static final Logger LOGGER = LoggerFactory.getLogger(ThreadLocalTest.class); private static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { Thread thread1 = new Thread(new Runnable() { @Override public void run() { threadLocal.set("thread1 local"); LOGGER.info(threadLocal.get()); threadLocal.remove(); LOGGER.info(threadLocal.get()); } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { threadLocal.set("thread2 local"); LOGGER.info(threadLocal.get()); } }); thread1.start(); thread2.start(); } }
输出:
19:24:20.720 [Thread-1] INFO com.example.demo.ThreadLocalTest - thread2 local 19:24:20.720 [Thread-0] INFO com.example.demo.ThreadLocalTest - thread1 local 19:24:20.723 [Thread-0] INFO com.example.demo.ThreadLocalTest - null
可以看到两个线程中的ThreadLocal变量互不干扰
2.原理
其类图如上,可以看到在每个线程中都有一个用于存储线程本地变量的Map,接下里分析ThreadLocal最重要的三个方法,看看ThreadLocal如何与ThreadLocalMap交互:
(1)set方法
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
set方法就是将值保存到当前调用该方法的线程ThreadLocalMap变量中,其中key为ThreadLocal引用,value为要保存的值,这样就可以解释为什么ThreadLocal变量能够在每个线程中互不干扰,因为保存在不同线程的ThreadLocalMap中。
(2)get方法
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(); }
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;
}
protected T initialValue() {
return null;
}
get方法则是从调用该方法的当前线程中拿到ThreadLocalMap,并以ThreadLocal为key,获取对应的值,其中如果没有获取到对应的value,则返回默认值null
(3)remove方法
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
remove方法也不如所料,从当前线程中的ThreadLocalMap中删除对应的ThreadLocal变量。
分析完源码,可以看到ThreadLocal本质上只是一个外壳类,不保存数据,数据都保存在Thread类中的ThreadLocalMap中,其中ThreadLocalMap并没有使用集合中的HashMap,而是实现了一个简单的开发地址法的Map。