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

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

我的公众号

JDK 1.8 源码分析之 ThreadLoad (线程变量)_第1张图片
微信公众号
    package com.gx.Thread;

import lombok.extern.slf4j.Slf4j;

/**
 * Created by GX on 2017/8/28.
 */
@Slf4j
public class TestThreadLoad {

    private static  ThreadLocal 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 holder = new ThreadLocal();

    /**
     * 在项目启动的时候将配置的读、写数据源加到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 (线程变量))