• 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流水账公众号
  • 相关阅读:
    caffe:使用C++来提取任意一张图片的特征(从内存读取数据)
    python:控制鼠标和键盘
    .dll 文件编写和使用
    python:打包成exe程序
    python:小乌龟turtle
    python:input()和raw_input()
    C++:哈希
    C++:线程(std::thread)
    GitHub:Git的使用
    链表
  • 原文地址:https://www.cnblogs.com/guofu-angela/p/9669194.html
Copyright © 2020-2023  润新知