美文网首页Jdk源码分析
JDK 1.8 源码分析之 ThreadLoad (线程变量)

JDK 1.8 源码分析之 ThreadLoad (线程变量)

作者: Gxgeek | 来源:发表于2017-08-28 10:37 被阅读53次

    ThreadLoad 这个变量 是我在接触AOP 动态 切换数据源的时候接触的 当时觉得很神奇,今天看下源码一探究竟下

    我的公众号

    微信公众号
        package com.gx.Thread;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * Created by GX on 2017/8/28.
     */
    @Slf4j
    public class TestThreadLoad {
    
        private static  ThreadLocal<String> local = new ThreadLocal<>();
    
        public static void main(String[] args) throws InterruptedException {
            local.set("main");
            log.info(local.get());
            Thread childThread  = new Thread(()->{
                local.set("child");
                log.info(local.get());
            });
            childThread.start();
            childThread.join();
            log.info(local.get());
        }
    }
    输出
    
    10:05:39.865 [main] INFO com.gx.Thread.TestThreadLoad - main
    10:05:39.912 [Thread-0] INFO com.gx.Thread.TestThreadLoad - child
    10:05:39.912 [main] INFO com.gx.Thread.TestThreadLoad - main
    
    

    可以看出 ThreadLoad 确实隔断了 多线程之间变量不安全的问题
    看下
    local 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);
        }
    
    

    大概是这个意思 拿到当前线程 已当前线程 去拿到 ThreadLocalMap map 为空就去 create

    看下create 的时候干了什么

    
        void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
       }
    
    

    Thread中有个变量为 threadLocals

    public class Thread implements Runnable {
    
        ThreadLocal.ThreadLocalMap threadLocals = null;
    }
    

    嗯 这下理解为什么 ThreadLoad 保证线程之间的安全了

    Thread 这个变量 的成员变量 threadLocals 是个Map表 执行 ThreadLoad set 方法时
    new 出一个 ThreadMap 已 当前的 ThreadLoad 为Key 值 我们的value set进去

    ThreadLocalMap 是ThreadLoad 的一个内部类

    下面贴出这个类的主要代码

    static class ThreadLocalMap {
    
    
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];// INITIAL_CAPACITY 初始容量 和HahshMpa 的一样是 16 
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
        
        
        private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
        
            Entry e = table[i];
        
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }
    
        
        
    }
    

    get 的代码

        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();
        }
    
    • 其实从上面看源码 ThreadLoad 其实实现很好容易懂
    • 以前的我总是觉得很深奥,其实还是没看源码 ,多看源码有助 与设计更好的程序
    • 还有 为什么我的 ThreadLoad 类喜欢用static 呢?
    • 因为 我们看到 ThreaLoad set get 的时候 是从当前的线程去寻找他的 map的 ,所以设计成static 知识 hashcode一 样而已 设计成static 只是 为了 少创建对象 节省内存空间而已。
    • 另外我们注意到 ThreadLoad 为每个线程创建 Map,可以想象一下在应用中大规模使用会怎么样。 所有 ThreadLoad 的很好用 但是要慎用 注意下使用场景。

    下面可以看下ThreadLoad 的使用场景

    · 动态切换数据源(结合spring aop 的 使用)

    /**
     * 数据源的Handler类
     */
    public class DataSourceHandler {
    
        // 数据源名称线程池
        public static final ThreadLocal<String> holder = new ThreadLocal<String>();
    
        /**
         * 在项目启动的时候将配置的读、写数据源加到holder中
         */
        public static void putDataSource(String datasource) {
            holder.set(datasource);
        }
    
        /**
         * 从holer中获取数据源字符串
         */
        public static String getDataSource() {
            return holder.get();
        }
    }
    
    

    这个类是AbstractRoutingDataSource Spring 提供的选择数据源的类
    有个

    /**
     * 获取数据源,用于动态切换数据源
     */
    public class ChooseDataSource extends AbstractRoutingDataSource {
    
    
        /**
         * 实现父类中的抽象方法,获取数据源名称
         * @return
         */
        protected Object determineCurrentLookupKey() {
            return DataSourceHandler.getDataSource();
        }
    
    }
    
    

    再结合AOP 使用 再AOP 中放入不同的数据源的名字spring在使用数据源的时候 会调用
    determineCurrentLookupKey() 拿到数据源查询数据 这样就可以动态的查处数据
    AOP 可以大家自己实现

    相关文章

      网友评论

        本文标题:JDK 1.8 源码分析之 ThreadLoad (线程变量)

        本文链接:https://www.haomeiwen.com/subject/azmwdxtx.html