ThreadLocal源码分析
ThreadLocal源码分析
一. 简介
ThreadLocal是JDK提供的一个工具类,其作用是在多线程共享资源的情况下,使每个线程持有一份该资源的副本,每个线程的副本都是独立互不影响的。线程操作各自的副本,这样就避免了资源竞争引发的线程安全问题。
二. 源码分析
ThreadLocal,线程本地变量,该变量为每个线程私有。ThreadLocal类有一个内部类,名为ThreadLocalMap,可以理解为一个简化版的HashMap,它的Key为ThreadLocal实例,Value为ThreadLocal对象所引用的值,源码如下:
static class ThreadLocalMap {
//该Map的Entry,Key为ThreadLocal实例,Value为ThreadLocal对象所引用的值。
//这里使用了弱引用,当Entry为null时可以尽快被GC
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//初始容量16
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
}
ThreadLocalMap内部保存了众多的ThreadLocal对象,那么既然说ThreadLocal是线程私有的,那么ThreadLocalMap是存放在哪里呢?
Thread类有一个成员变量——threadLocals,它就是保存了与当前Thread关联的一个ThreadLocalMap,源码如下:
//当前线程内部维护的ThreadLocalMap对象,用于保存所有ThreadLocal实例
ThreadLocal.ThreadLocalMap threadLocals = null;
可以看到,ThreadLocalMap对象保存在了Thread的内部,也即当前线程的私有内存中。
ThreadLocal的主要方法为get()、set()和initialValue()。首先看set():
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程关联的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
//创建一个Entry,以当前ThreadLocal对象为Key,待存储对象为Value,保存在ThreadLocalMap中
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
可以看到,set()的逻辑很简单,从当前线程中获取ThreadLocalMap,然后将该ThreadLocal的值保存在里面。
再看get()方法:
public T get() {
//获取当前线程对象
Thread t = Thread.currentThread();
//获取当前线程关联的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
//从ThreadLocalMap获取以ThreadLocal为key的Entry的value
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果当前ThreadLocalMap不存在,则调用setInitialValue()方法,获取初始值
return setInitialValue();
}
ThreadLocal还有一个方法initialValue(),该方法提供给子类覆盖,以在创建ThreadLocal时指定初始值。
四. 应用场景
ThreadLocal最常见的使用场景为管理数据库连接Connection对象等。Spring中使用ThreadLocal来设计TransactionSynchronizationManager类,实现了事务管理与数据访问服务的解耦,同时也保证了多线程环境下connection的线程安全问题。
五. 注意事项
由于ThreadLocal对象会在每个线程中创建一个副本,所以在多线程环境下,如果ThreadLocal所保存的对象过大,可能导致内存的过多占用,需要引起注意。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【redis6.0.6】深入源码,一探究竟 -- redis服务器开机自启动
- 不是你记忆中的单例模式,但适用的程度,更胜一筹
- 安卓模拟器修改hosts文件
- 为容器化的 Postgres 数据库启用 ssl 连接
- 【SpringBoot web-1】web项目数据校验
- 【SpringBoot web-2】web项目参数传递
- 筛法求素数
- redis学习(十三)
- 【SpringBoot web-3】web项目统一数据封装与全局异常处理
- 送你 8 张图,好好理解一下。
- 【前端系列-1】ajax与Springboot通信将数据库数据渲染到前端表格
- 【前端系列-2】layui+springboot实现表格增删改查
- 容器技术|Docker三剑客之docker-swarm
- 【前端系列-3】layui表格使用自定义模板templet
- 【前端系列-4】layui表格集成select选择框和switch开关