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 可以大家自己实现
网友评论