1.ThreadLocal的使用
ThreadLocal,即线程变量,是一个以ThreadLocal对象为键,任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据ThreadLocal对象查询到绑定到这个线程上的值。它的作用就是为每一个线程都创建一个变量副本,并且线程可以修改自己的变量副本,不影响其他线程的变量副本,使得各个线程之间的变量互不干扰。
如下代码:
public class ThreadLocalDemo { private static final ThreadLocal<Long> TIME_THREADLOCAL = new ThreadLocal<Long>(); public static final void begin(){ TIME_THREADLOCAL.set(System.currentTimeMillis()); } public static final long end(){ return System.currentTimeMillis()-TIME_THREADLOCAL.get(); } public static void main(String[] args) throws InterruptedException { ThreadLocalDemo.begin(); TimeUnit.SECONDS.sleep(1); System.out.println(ThreadLocalDemo.end()); } }
运行结果:
1001
2.ThreadLocal内部结构
从上图可知:每个Thread内部都有一个Map,即ThreadLocalMap;ThreadLocalMap中存放ThreadLocal对象作为key,变量的副本作为value;Thread内部的Map是由ThreadLocal维护的,由Thread向Map中设置和获取对应线程的变量值。
3.源码分析
3.1 ThreadLocal.set方法
public void set(T value) { Thread t = Thread.currentThread();
// ThreadLocal.ThreadLocalMap是线程t的一个变量,同时由源码可知,ThreadLocalMap底层是一个数组链表结构(和HashMap类似) ThreadLocalMap map = getMap(t); if (map != null)
// 以ThreadLocal对象为key,要存放的对象value为value,存放在ThreadLocalMap中,进入该方法 map.set(this, value); else createMap(t, value); }
private void set(ThreadLocal key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); // 通过下标i来定位数组中对应的位置,然后判断是否存在相同的key值 for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal k = e.get(); // 如果存在相同的key值,则将value替换 if (k == key) { e.value = value; return; } // 如果key == null,则做另外一种处理 if (k == null) { replaceStaleEntry(key, value, i); return; } } // 如果以上都不满足,则创建一个Entry对象 tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
3.2.ThreadLocal.get方法
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) {
// 根据ThreadLocal对象获取Entry对象 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null)
// 获取value return (T)e.value; } return setInitialValue(); }