当前位置: 首页 > news >正文

JDK源码——ThreadLocal

在这里插入图片描述

提供的方法

remove(): 移除当前线程的局部变量值。调用此方法后,当前线程将不再持有任何局部变量值。
set(T): 为当前线程设置一个新的局部变量值。参数T是要设置的值的类型。
get(): 获取当前线程的局部变量值。返回类型为T。
withInitial(Supplier): 创建一个新的ThreadLocal实例,其初始值由提供的Supplier提供。这个方法返回一个新的ThreadLocal对象,其类型为S。

应用场景

ThreadLocal主要应用于以下场景:

  1. 数据库连接管理:在多线程环境下,每个线程都需要独立的数据库连接。使用ThreadLocal可以确保每个线程都有自己的数据库连接,避免了多个线程共享同一个连接导致的问题。

  2. SimpleDateFormat线程安全问题:SimpleDateFormat类不是线程安全的,如果在多线程环境下使用同一个SimpleDateFormat实例进行日期格式化,可能会导致数据错乱。通过使用ThreadLocal为每个线程提供一个独立的SimpleDateFormat实例,可以避免这个问题。

  3. Spring事务管理:Spring框架中的事务管理器(TransactionManager)通常使用ThreadLocal来存储当前线程的事务信息。这样可以确保在多线程环境下,每个线程都能正确地处理自己的事务。

  4. 用户身份验证和会话管理:在Web应用程序中,通常需要对每个用户进行身份验证并维护用户的会话信息。使用ThreadLocal可以在每个线程中存储用户的身份信息和会话信息,使得这些信息在整个请求处理过程中都可以被访问到。

  5. 缓存管理:在多线程环境下,可以使用ThreadLocal来存储每个线程的缓存数据,避免不同线程之间的缓存数据冲突。

示例

下面是一个使用ThreadLocal的简单示例,演示了如何在多线程环境下为每个线程提供独立的数据库连接:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class DatabaseConnectionManager {private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<>();public static Connection getConnection() throws SQLException {Connection connection = connectionHolder.get();if (connection == null) {// 创建一个新的数据库连接connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "username", "password");connectionHolder.set(connection);}return connection;}public static void closeConnection() throws SQLException {Connection connection = connectionHolder.get();if (connection != null) {connection.close();connectionHolder.remove();}}
}

在这个示例中,我们使用了ThreadLocal来存储每个线程的数据库连接。当调用getConnection()方法时,首先检查当前线程是否已经有一个数据库连接,如果没有,则创建一个新的连接并将其存储在ThreadLocal中。这样,每个线程都可以独立地访问和关闭自己的数据库连接,避免了资源共享的问题。

原理

ThreadLocal的实现原理主要基于Java的线程局部变量。每个线程都有一个自己的线程局部变量副本,这个副本只能由当前线程访问,其他线程无法访问。

具体来说,ThreadLocal通过创建一个静态的内部类ThreadLocalMap来实现线程局部变量的存储。每个线程都会拥有一个自己的ThreadLocalMap实例,这个实例中存储了线程的局部变量。ThreadLocalMap是一个自定义的哈希映射表,它的键是ThreadLocal对象,值是线程的局部变量。

当调用ThreadLocal的set方法时,它会将值存储在当前线程的ThreadLocalMap中,以ThreadLocal对象作为键。而get方法则是从当前线程的ThreadLocalMap中获取与ThreadLocal对象关联的值。

由于每个线程都有自己独立的ThreadLocalMap,因此每个线程只能访问到自己的局部变量,从而实现了线程之间的隔离。

需要注意的是,虽然ThreadLocal可以实现线程之间的隔离,但它并不能解决多线程环境下的资源共享问题。如果多个线程共享同一个ThreadLocal实例,那么它们仍然会共享相同的值。因此,在使用ThreadLocal时,需要确保每个线程都有自己的ThreadLocal实例,以避免资源竞争和数据不一致的问题。

另外,ThreadLocal还提供了一些额外的方法,如remove和withInitial。remove方法用于从当前线程的ThreadLocalMap中移除与ThreadLocal对象关联的值,以便及时释放资源。而withInitial方法则用于创建一个新的ThreadLocal实例,并指定一个初始值。

源码阅读

属性

public class ThreadLocal<T> {/*** ThreadLocals依赖于每个线程附加的线性探测哈希映射(Thread.threadLocals和inheritableThreadLocals)。* ThreadLocal对象充当键,通过threadLocalHashCode进行搜索。这是一个自定义哈希码(仅在ThreadLocalMap中有用),* 它可以消除连续构造的ThreadLocals被同一线程使用时的冲突,同时在较少见的情况下保持良好行为。*/private final int threadLocalHashCode = nextHashCode();/*** 下一个要分配的哈希码。原子更新。从零开始。*/private static AtomicInteger nextHashCode = new AtomicInteger();/*** 连续生成的哈希码之间的差值 - 将隐式顺序线程局部ID转换为接近最优分布的乘法哈希值,适用于2的幂大小的表。*/private static final int HASH_INCREMENT = 0x61c88647;
}

set

这段代码是一个Java方法,用于设置当前线程中ThreadLocal变量的值。大多数子类不需要重写此方法,只需依赖initialValue方法来设置线程局部变量的值。

/*** 将当前线程中此线程局部变量的副本设置为指定值。大多数子类无需重写此方法,* 仅依赖{@link #initialValue}方法来设置线程局部变量的值。** @param value 要存储在当前线程中此线程局部变量副本的值。*/
public void set(T value) {Thread t = Thread.currentThread(); // 获取当前线程ThreadLocalMap map = getMap(t); // 获取与当前线程关联的ThreadLocalMapif (map != null) { // 如果ThreadLocalMap不为空map.set(this, value); // 在ThreadLocalMap中设置当前ThreadLocal对象的值} else {createMap(t, value); // 如果ThreadLocalMap为空,则创建一个新的ThreadLocalMap并设置值}
}

这个方法首先获取当前线程,然后尝试从与该线程关联的ThreadLocalMap中获取当前ThreadLocal对象的映射。如果映射存在,就在映射中设置值。如果映射不存在,就创建一个新的ThreadLocalMap并设置值。

get

/*** 返回当前线程中此线程局部变量的值。如果当前线程中该变量没有值,* 则首先将其初始化为调用{@link #initialValue}方法所返回的值。** @return 此线程局部的当前线程值*/
public T get() {Thread t = Thread.currentThread(); // 获取当前线程ThreadLocalMap map = getMap(t); // 获取与当前线程关联的ThreadLocalMapif (map != null) { // 如果ThreadLocalMap不为空ThreadLocalMap.Entry e = map.getEntry(this); // 从ThreadLocalMap中获取当前ThreadLocal对象的条目if (e != null) { // 如果条目不为空@SuppressWarnings("unchecked")T result = (T)e.value; // 获取条目中的值,并转换为泛型类型Treturn result; // 返回结果}}return setInitialValue(); // 如果没有找到值,则初始化并返回值
}

这个方法首先获取当前线程,然后尝试从与该线程关联的ThreadLocalMap中获取当前ThreadLocal对象的条目。如果找到了条目,就返回条目中的值。如果没有找到条目,就调用setInitialValue方法来初始化值,并返回这个初始值。

getMap和createMap

明白,以下是翻译后的内容:

/*** 获取与ThreadLocal关联的映射。在InheritableThreadLocal中被覆盖。** @param  t 当前线程* @return 映射*/
ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}/*** 创建与ThreadLocal关联的映射。在InheritableThreadLocal中被覆盖。** @param t 当前线程* @param firstValue 映射的初始条目的值*/
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}

getMap方法用于获取当前线程上的ThreadLocal映射。这个映射包含了与该线程绑定的ThreadLocal变量的副本。如果在InheritableThreadLocal类中有相应的实现,那么这个方法将会被重写。

createMap方法用于在当前线程上创建一个新的ThreadLocal映射,并将传入的firstValue作为这个映射的初始值。这个映射会将当前ThreadLocal对象作为键,firstValue作为值。如果在InheritableThreadLocal类中有相应的实现,那么这个方法也会被重写。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 《光与夜之恋》3D建模含量超标,纯炫技还是释放新信号?
  • 你和NumPy之间,只差这40张图
  • Unity教程(十)Tile Palette搭建平台关卡
  • Spring自动注册-自定义标签解析
  • 【综合架构】存储服务 NFS
  • Unity 资源分享 之 恐龙Ceratosaurus资源模型携 82 个动画来袭
  • MongoDB基础
  • 自注意力Self-attention
  • 鼠标为什么要放在鼠标垫上才好用?/ 鼠标的工作原理
  • 排序【归并排序和计数排序】
  • 【图像去雾系列】使用暗通道先验去雾算法对图像进行去雾处理
  • 刷题记录第109天-K个一组反转链表
  • 你知道AI模型是如何学习的吗?
  • keepalived安装-集群部署
  • 【面试题】接雨水
  • [iOS]Core Data浅析一 -- 启用Core Data
  • [译]Python中的类属性与实例属性的区别
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • Akka系列(七):Actor持久化之Akka persistence
  • C++类的相互关联
  • C++类中的特殊成员函数
  • Debian下无root权限使用Python访问Oracle
  • Docker入门(二) - Dockerfile
  • download使用浅析
  • ECMAScript 6 学习之路 ( 四 ) String 字符串扩展
  • ERLANG 网工修炼笔记 ---- UDP
  • golang中接口赋值与方法集
  • log4j2输出到kafka
  • node 版本过低
  • python 学习笔记 - Queue Pipes,进程间通讯
  • Vim 折腾记
  • 爱情 北京女病人
  • 浮动相关
  • 规范化安全开发 KOA 手脚架
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 入门到放弃node系列之Hello Word篇
  • 数组的操作
  • 源码安装memcached和php memcache扩展
  • 阿里云API、SDK和CLI应用实践方案
  • #Datawhale AI夏令营第4期#AIGC方向 文生图 Task2
  • #LLM入门|Prompt#3.3_存储_Memory
  • (0)Nginx 功能特性
  • (02)Unity使用在线AI大模型(调用Python)
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (7)STL算法之交换赋值
  • (C++17) std算法之执行策略 execution
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (二)springcloud实战之config配置中心
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (三)docker:Dockerfile构建容器运行jar包
  • (十七)Flink 容错机制
  • (十五)使用Nexus创建Maven私服
  • (学习总结16)C++模版2
  • (转)eclipse内存溢出设置 -Xms212m -Xmx804m -XX:PermSize=250M -XX:MaxPermSize=356m
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default