当前位置:网站首页>ThreadLocal源码解析及使用场景
ThreadLocal源码解析及使用场景
2022-08-03 11:22:00 【代码大师麦克劳瑞】
概述
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
ThreadLocal类用来提供线程内部的副本变量。这种变量在多线程环境下访问(通过get和set方法访问)时能保证各个线程的变量相对独立于其他线程内的变量。ThreadLocal实例通常来说都是private static类型的,用于关联线程和线程上下文。
源码浅析
方法声明 | 描述 |
---|---|
public ThreadLocal() | 创建线程副本变量 |
public T get() | 获取当前线程副本变量 |
public void set(T value) | 设置当前线程副本变量 |
public void remove() | 移出当前线程绑定副本变量 |
我们一起看下这几个关键方法,我将注释直接标记在代码上方便理解
public class ThreadLocal<T> {
/** * ThreadLocals rely on per-thread linear-probe hash maps attached * to each thread (Thread.threadLocals and * inheritableThreadLocals). The ThreadLocal objects act as keys, * searched via threadLocalHashCode. This is a custom hash code * (useful only within ThreadLocalMaps) that eliminates collisions * in the common case where consecutively constructed ThreadLocals * are used by the same threads, while remaining well-behaved in * less common cases. */
private final int threadLocalHashCode = nextHashCode();
/** * The next hash code to be given out. Updated atomically. Starts at * zero. */
private static AtomicInteger nextHashCode =
new AtomicInteger();
/** * The difference between successively generated hash codes - turns * implicit sequential thread-local IDs into near-optimally spread * multiplicative hash values for power-of-two-sized tables. */
private static final int HASH_INCREMENT = 0x61c88647;
/** * Returns the next hash code. */
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
protected T initialValue() {
return null;
}
/** * Creates a thread local variable. * @see #withInitial(java.util.function.Supplier) */
public ThreadLocal() {
}
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */
public T get() {
//获得对应的线程
Thread t = Thread.currentThread();
//通过线程获得对应的map
java.lang.ThreadLocal.ThreadLocalMap map = getMap(t);
//判断map是否为空,不为空则拿到对应线程作为Key的value
if (map != null) {
java.lang.ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//map为空,返回初始值
return setInitialValue();
}
/** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @return the initial value */
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
java.lang.ThreadLocal.ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
if (this instanceof TerminatingThreadLocal) {
TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
}
return value;
}
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */
public void set(T value) {
Thread t = Thread.currentThread();
//通过当前线程,获得map然后进行map的set方法
java.lang.ThreadLocal.ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
//没有则创建新的map
createMap(t, value);
}
}
/** * Removes the current thread's value for this thread-local * variable. If this thread-local variable is subsequently * {@linkplain #get read} by the current thread, its value will be * reinitialized by invoking its {@link #initialValue} method, * unless its value is {@linkplain #set set} by the current thread * in the interim. This may result in multiple invocations of the * {@code initialValue} method in the current thread. * * @since 1.5 */
public void remove() {
java.lang.ThreadLocal.ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null) {
m.remove(this);
}
}
}
简单来说,ThreadLocal就像一个大的仓库,每个线程有一个自己的保险柜,线程可以通过自己的唯一标识找到自己的保险柜,并且在自己的保险柜中存取东西,随着线程的消失,自己的保险柜也消失了。代码并不难,很容易看懂。
工具类
下面是个简单的工具类,需要自取。工具类封装了需要用到的基本方法,其他请自行拓展。
import java.util.HashMap;
import java.util.Map;
/*** * scott */
public class ThreadLocalUtil {
private static final ThreadLocal<Map<Object, Object>> THREAD_LOCAL = new ThreadLocal<Map<Object, Object>>() {
@Override
protected Map<Object, Object> initialValue() {
return new HashMap<Object, Object>();
}
};
/** * 根据key获取值 * * @param key * @return */
public static Object getValue(Object key) {
if (THREAD_LOCAL.get() == null) {
return null;
}
return THREAD_LOCAL.get().get(key);
}
/** * 存储 * * @param key * @param value * @return */
public static Object setValue(Object key, Object value) {
Map<Object, Object> cacheMap = THREAD_LOCAL.get();
if (cacheMap == null) {
cacheMap = new HashMap<Object, Object>();
THREAD_LOCAL.set(cacheMap);
}
return cacheMap.put(key, value);
}
/** * 根据key移除值 * * @param key */
public static void removeValue(Object key) {
Map<Object, Object> cacheMap = THREAD_LOCAL.get();
if (cacheMap != null) {
cacheMap.remove(key);
}
}
/** * 重置 */
public static void reset() {
if (THREAD_LOCAL.get() != null) {
THREAD_LOCAL.get().clear();
}
}
public static boolean containsKey(Object key) {
return THREAD_LOCAL.get().containsKey(key) ? Boolean.TRUE : Boolean.FALSE;
}
}
使用场景
举个栗子:
1.数据源切换。
2.多层之间参数传递,例如微服务请求的requestId(或traceId)
其实不难看出,其使用场景都是围绕着他的原理进行的,在微服务模块中用于唯一标记一次请求的全局唯一UUID,在不同模块中传递相同的traceid是通过RPC框架封装并且序列化/反序列化实现的,而RPC框架反序列化出traceId后就会将其放入到ThreadLocal中,这样在模块的各个阶段记录log时都可以通过该ID标识出该请求。并且随着微服务框架的完成,也可以通过一个服务将traceId串联起来,分析一次请求中各个阶段的状态以及耗时。
我最近遇到一个使用场景就是,我在调用其他方法时,想传入一个参数,但是又不想重写方法增加入参,因此使用这个工具类,在调用之前设置了当前用户id,然后直接侵入调用方法,拿到ThreadLocal进行判断并且获取变量。
public void init(ProjectScoreEmpower projectScoreEmpower, int year, int quarter, Project project) throws Exception {
Long userId = projectScoreEmpower.getEmpowerUserId();
// 这里将user_id设置进当前线程副本变量
ThreadLocalUtil.setValue("current_user_id", userId);
String projectId = project.getUuid();
}
//这是另一层的代码中的私有方法
private String getCurrentUserId(){
//这里我先从当前线程副本变量中判断并获取当前用户id
if(ThreadLocalUtil.containsKey("current_user_id")){
String current_user_id = ThreadLocalUtil.getValue("current_user_id").toString();
return current_user_id;
}
//没有,说明上层未传递,则通过其他方式获取。
return userDataManager.getCurrentUserTypeAndId();
}
怎么样?非常简单吧!!!
点赞收藏,富婆包养
边栏推荐
- 矩阵的计算[通俗易懂]
- 【MySQL功法】第2话 · 数据库与数据表的基本操作
- Simple implementation of a high-performance clone of Redis using .NET (1)
- 【一起学Rust】Rust学习前准备——注释和格式化输出
- MapReduce中ETL数据清洗案例
- 本周四晚19:00知识赋能第4期直播丨OpenHarmony智能家居项目之设备控制实现
- 【文件IO的简单实现】
- 浅谈SVN备份
- build --repot
- Classical Architecture and Memory Classification of Embedded Software Components
猜你喜欢
随机推荐
Lease recovery system based on PHP7.2+MySQL5.7
下午见!2022京东云数据库新品发布会
劝退背后。
【MySQL】数据库进阶之索引内容详解(上篇 索引分类与操作)
通过组策略安装软件和删除用户配置文件
build --repot
Dry goods!A highly structured and sparse linear transformation called Deformable Butterfly (DeBut)
[Detailed explanation of binary search plus recursive writing method] with all the code
嵌入式软件组件经典架构与存储器分类
ETL data cleaning case in MapReduce
Why is the new earth blurred, in-depth analysis of white balls, viewing pictures, and downloading problems
多态详细讲解(简单实现买票系统模拟,覆盖/重定义,多态原理,虚表)
巴比特 | 元宇宙每日必读:玩家离场,平台关停,数字藏品市场正逐渐降温,行业的未来究竟在哪里?...
SmobilerService 推送实现
[Wrong title] Circuit maintenance
[论文阅读] (23)恶意代码作者溯源(去匿名化)经典论文阅读:二进制和源代码对比
Generate interface documentation online
Traceback (most recent call last): File
CADEditorX ActiveX 14.1.X
增加WebView对localStorage的支持