这段时间在重构项目的代码,把项目主体的实现方式做了调整,ThreadLocal在其中扮演了非常重要的角色。
需求(使用场景):客户端有一个请求过来,Java程序根据请求报文的参数决定调用某个服务提供数据。相信大家都会有类似的需求,拿我们业务来说,请求报文如下:
{ "appid": "88888888", "userid": 80, "datatype": "***", "data":"{"dataids":[146,147,148]}" }
一个用户请求过来,需要根据datatype字段决定调不同的服务。
网上真的很多乱七八糟文章,讲ThreadLocal中的ThreadLocalMap存放的key是线程对象,value是设置的线程局部变量,觉得挺有道理的,正好实现了线程数据隔离。但是仔细看源码。发现每个Thread对象都维护了一个私有的ThreadLocalMap对象,ThreadLocalMap对象key是ThreadLocal对象,value是设置的变量值。这就有点扯了,看下面的示例代码,反正这样设计我是看不出来有什么好处,因为每个线程中会放置N个变量,那我就new N个ThreadLocal对象,Thread中的ThreadLocalMap维护的map key存放ThreadLocal对象,value放置值。
public class ThreadLocal<T> { public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); //拿到线程私有的ThreadLocalMap if (map != null) map.set(this, value); //Thread对象中 map key是ThreadLocal对象 else createMap(t, value); } 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(); } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); //hashcode & 运算去除高位 table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); } }
所以看到源码之后,那些讲ThreadLocal销毁之后,Thread -> ThreadLoalMap -> Entry[ WeakRefrence, Object] 因为线程还在,线程维护的Map的Entry数组也还存在,那数组中Entry的key是ThreadLocal的弱引用,value是值。所以会有内容泄漏的风险,所以最好是在使用完之后手动调用remove()函数。
至于源码中那些涉及Map在数组位置的求值,rehash我觉得都差不多,因为16的初始大小我认为是够的,因为很少会使用创建16个ThreadLocal对象。
public static void main(String[] args) { ThreadLocal<Person> threadLocal = new ThreadLocal<>(); //每个线程局部变量都需要创建一个ThreadLocal对象 ThreadLocal<String> threadLocal2 = new ThreadLocal<>(); AtomicInteger atomicInteger = new AtomicInteger(1000); ExecutorService executorService = Executors.newCachedThreadPool();
//for(int i 0->10) executorService.submit(() -> { Person person = new Person(); person.setIdcard(atomicInteger.getAndIncrement()); person.setName(Thread.currentThread().getName()); threadLocal.set(person); threadLocal2.set("haha"); System.out.println(threadLocal.get()); System.out.println(threadLocal2.get()); System.out.println(); threadLocal.remove(); }); executorService.shutdown(); }