ThreadLocal实践

  这段时间在重构项目的代码,把项目主体的实现方式做了调整,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(); }
欢迎关注Java流水账公众号
原文地址:https://www.cnblogs.com/guofu-angela/p/9669194.html