ThreadLocal类
用于为线程提供一份本地变量,仅限某一线程自身使用,用这种牺牲空间的方式换取了并发的效率。
但ThreadLocal并不是设计来解决并发安全问题的方法,每条线程所使用的都是独立的副本,ThreadLocal实例在每条线程中的表现可能都不同,也正因为这个特性,可以被用于设计web应用中session,每个用户会话连接web容器会为其分配一条线程,每条线程中的session也不相同。
验证ThreadLocal独立于每条线程的特性:
package test; import java.util.HashMap; import java.util.concurrent.*; public class Test implements Callable<String>{ private static final ThreadLocal<String> nameLocal = new ThreadLocal<>(); private static final HashMap<String ,Integer> hashMap = new HashMap<>(); public static void main(String[] args) throws ExecutionException, InterruptedException { Test t = new Test(); ExecutorService executorService = Executors.newCachedThreadPool(); Future<String> futureTask = executorService.submit(t); Future<String> futureTask1 = executorService.submit(t); System.out.println(futureTask.get()); System.out.println(futureTask1.get()); } @Override public String call() throws Exception { if(nameLocal.get() == null){ int id = (int)(Math.random()*1000); nameLocal.set("id"+id); } if(hashMap.size() == 0){ hashMap.put("key",(int)(Math.random()*1000)); } Integer value = hashMap.get("key"); return nameLocal.get()+" "+value; } }
结果:
从结果看,nameLocal显然在不同线程中分别被创建了一次,每条线程运行的时候为其分配了一个初始化的空nameLocal,而同样声明为static final的hashMap一共被创建了一次,最终在两条线程中是完全相同的hashMap。以上代码重复运行多次总有上述现象,也排除了一些运行时的偶然因素。
从源码上看:
ThreadLocal类set源码:
//ThreadLocal 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); }
Thread类成员变量threadLocals声明:
//Thread成员变量threadLocals声明 /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal的内部类ThreadLocalMap中的set源码:
//ThreadLocalMap set /** * Set the value associated with key. * * @param key the thread local object * @param value the value to be set */ 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); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
从上边的源码可以看出,set方法将变量放进了名为ThreadLocalMap类(实际上该类是ThreadLocal的内部类)的实例中,而该内部类的实例又是从Thread类实例的成员变量拿到的。可以认为,ThreadLocal之所以能保证对每条线程独立的原因之一,就是用于储存数据的ThreadLocalMap对每条线程保持了独立。
综述:
ThreadLocal用来隔离数据,通过防止多个线程对同一数据进行读写提高了效率,也方便了在同一线程中的传参。但是和synchronized的功能有本质上的区别