ThreadLocal实例应用

threadlocal相信大家都用过,但你知道他的原理吗,今天了不起带大家学习threadlocal。
threadlocal是什么在多线程编程中,经常会遇到需要在不同线程中共享数据的情况。通常情况下,为了保证线程安全,我们需要使用锁或其他同步机制。然而,有些情况下,我们希望在每个线程中都有一份独立的数据副本,这就是threadlocal派上用场的地方。
threadlocal翻译过来就是线程本地,也就是本地线程变量,意思是threadlocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。threadlocal提供了一种机制,允许我们为每个线程创建独立的变量,每个线程都可以独立访问自己的变量,而不会干扰其他线程的数据。threadlocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量,各个线程间互不影响,从而实现线程安全。
threadlocal的原理threadlocal的原理涉及到两个重要概念:threadlocal实例和threadlocalmap。
1. threadlocal实例每个threadlocal对象实际上是一个容器,用于存储线程本地的变量副本。每个线程都可以拥有自己的threadlocal实例,这些实例可以存储不同的数据,互相之间互不影响。
2. threadlocalmapthreadlocalmap是threadlocal的底层数据结构,它是一个哈希表。每个线程都有一个与之相关联的threadlocalmap,用于存储该线程所拥有的threadlocal实例以及对应的值。threadlocalmap中的键是threadlocal实例,值是该线程对应threadlocal实例的变量副本。
当我们调用threadlocal的set()方法设置值时,实际上是在当前线程的threadlocalmap中以threadlocal实例为键,将值存储在对应的位置。而调用get()方法时,则是从当前线程的threadlocalmap中根据threadlocal实例获取对应的值。
threadlocal怎么用,应用场景public static void main(string[] args) { intstream.range(1, 5).foreach(i - > new thread(() - > { // 设置线程中本地变量的值 threadlocal.set(thread- + i); // 打印当前线程中本地内存中本地变量的值 system.out.println(threadlocal.get()); // 清除本地内存中的本地变量 threadlocal.remove(); // 打印本地变量 system.out.println(thread- + i + after remove: + threadlocal.get()); }).start());}/*thread-1thread-4thread-4 after remove: nullthread-2thread-3thread-2 after remove: nullthread-1 after remove: nullthread-3 after remove: null*/从结果可以看到,每一个线程都有各自的值,并且互不影响。
应用场景用户访问之后,在本地线程保存用户的身份信息,在本次访问过程中,可以随时获取用户的身份信息以验证身份。在进行对象跨层传递的时候,使用threadlocal可以避免多次传递,打破层次间的约束。数据库连接管理:每个线程可以拥有自己的数据库连接,避免了线程之间的数据库连接混淆。用户身份管理:在web应用中,可以将用户身份信息存储在threadlocal中,以便在整个请求处理过程中方便地访问。事务管理:将事务状态存储在threadlocal中,确保每个线程都能独立管理自己的事务状态。threadlocal源码分析先看一下 threadlocal 和 thread 的关系
thread类中有一个threadlocals属性,是threadlocal内部类threadlocalmap类型的变量,threadlocalmap可以看作是一个hashmap,其内部有一个内部类为 entry,继承了weakreference大致了解了thread和threadlocal的关系之后,看一下thread local的源码:我们只要看其主要的几个方法,就可以完全了解threadlocal的原理了。
set方法public void set(t value) { // 获取当前线程 thread t = thread.currentthread(); // 通过当前线程获取线程中的threadlocal.threadlocalmap对象 threadlocalmap map = getmap(t); if (map != null) // map不为空,则直接赋值 map.set(this, value); else // map为空,则创建一个threadlocalmap对象 createmap(t, value);}// 根据提供的线程对象,和指定的值,创建一个threadlocalmap对象void createmap(thread t, t firstvalue) { t.threadlocals = new threadlocalmap(this, firstvalue);}// threadlocals是thread类的一个属性threadlocalmap getmap(thread t) { return t.threadlocals;}/*thread 类 182行 // threadlocal values pertaining to this thread. this map is maintained by the threadlocal class. 与该线程有关的threadlocal值。这个映射由threadlocal类维护 threadlocal.threadlocalmap threadlocals = null;*/get方法// threadlocalmap中的内部类,存放key,valuestatic class entry extends weakreference { // 与此threadlocal关联的值 object value; // k:threadlocal的引用,被传递给weakreference的构造方法 entry(threadlocal k, object v) { super(k); value = v; }}public t get() { // 获取当前线程 thread t = thread.currentthread(); // 通过当前线程获取线程中的threadlocal.threadlocalmap对象 threadlocalmap map = getmap(t); if (map != null) { // map不为空,通过this(当前对象,即threadlocal对象)获取entry对象 threadlocalmap.entry e = map.getentry(this); if (e != null) { @suppresswarnings(unchecked) t result = (t)e.value; // entry不为空,则直接返回entry中的value值 return result; } } // 如果map或entry为空,则返回初始值-null return setinitialvalue();}// 设置初始值,初始化threadlocalmap对象,并设置value为 nullprivate t setinitialvalue() { // 初始化值,此方法返回 null t value = initialvalue(); thread t = thread.currentthread(); threadlocalmap map = getmap(t); if (map != null) map.set(this, value); else createmap(t, value); return value;}remove方法public void remove() { // 通过当前线程获取线程中的threadlocal.threadlocalmap对象 threadlocalmap m = getmap(thread.currentthread()); if (m != null) // 移除对象 m.remove(this);}// 根据key,删除对应的所有值private void remove(threadlocal key) { entry[] tab = table; int len = tab.length; // 获取key对应的 entry[] 下标 int i = key.threadlocalhashcode & (len-1); for (entry e = tab[i]; e != null; // 获取下一个entry对象 e = tab[i = nextindex(i, len)]) { if (e.get() == key) { e.clear(); // 通过重新哈希位于staleslot和下一个null插槽之间的任何可能冲突的条目,来清除陈旧的条目。这还会清除尾随null之前遇到的所有其他过时的条目,防止出现内存泄漏问题 expungestaleentry(i); return; } }}总结:
每个thread维护着一个threadlocalmap的引用threadlocalmap是threadlocal的内部类,用entry来进行存储threadlocal创建的副本是存储在自己的threadlocals中的,也就是自己的threadlocalmap。threadlocalmap的键为threadlocal对象,而且可以有多个threadlocal变量,因此保存在map中在进行get之前,必须先set,否则会报空指针异常,当然也可以初始化一个,但是必须重写initialvalue()方法。threadlocal本身并不存储值,它只是作为一个key来让线程从threadlocalmap获取value。注意事项虽然threadlocal在某些情况下非常有用,但过度使用它也可能导致内存泄漏问题。因为threadlocalmap中的数据只有在线程结束时才会被释放,如果没有正确地清理threadlocal实例,就可能会导致无限制的数据积累。
另外,threadlocal不适合在并发量非常大的情况下使用,因为每个线程都会创建自己的变量副本,可能会导致内存消耗过大。
threadlocal内存泄漏问题在上面的代码中,我们可以看出,当前threadlocal的引用k被传递给weakreference的构造函数,所以threadlocalmap中的key为threadlocal的弱引用。
如果当前线程一直存在且没有调用该threadlocal的remove方法,如果这个时候别的地方还有对threadlocal的引用,那么当前线程中的threadlocalmap中会存在对threadlocal变量的引用和value对象的引用,是不会释放的,会造成内存泄漏。
threadlocalmap中的entry的key使用的是threadlocal对象的弱引用,在没有其他地方对threadloca依赖,threadlocalmap中的threadlocal对象就会被回收掉,但是对应的value值不会被回收,这个时候map中就可能存在key为null但是value不为null的项,也会造成内存泄漏。
小结threadlocal是一种多线程编程的工具,可以帮助我们在多线程环境中管理线程本地的变量。它通过threadlocal实例和threadlocalmap的组合实现了这一功能。
使用threadlocal时需要注意内存泄漏和性能问题,确保合理使用。
使用完threadlocal后,一定执行remove操作,避免出现内存泄漏情况。

对抗性样本真的是不自然且无意义的吗?
汽车电子硬件:在用证明的示例
介绍100G QSFP28光模块各大品牌型号
回顾2022:千视大事记盘点!
飞思卡尔三款无线充电参考设计革新电池充电方式
ThreadLocal实例应用
第三代区块链主网FUTUREPIA将解决现有区块链生态系统的问题
电压跟随器电路原理、作用及应用
京东方的面板离三星还有多少差距
电源标准简介
AI怎样助力天气预报?怎么才能落地于天气预报?
一种改善COE反射光分色现象的技术解读
广州数控的工业机器人发展之路浅析,保持创新是关键!
贴片led拆卸技巧_贴片LED维修
带校准输入的热插拔控制器可精确监控两个负载电流
智能制造2025什么意思_2025两个制造是什么(中国制造)
魅族pro7什么时候上市?魅族pro7最新消息:魅族pro7即将上市,魅族Pro7首发十核处理器!小米笑翻了
浅谈盖勒普高柔性自动化制造控制管理系统方案
中环股份表示无锡工厂预计2020年第一季度开始投产 目前在国内占有率超过80%
分布式VxWorks/Linux/Android开发测试环境的实现与探索