ThreadLocal浅解

ThreadLocal是什么

Map(key - ThreadLocal实例,value - 存储的Object)

ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。
ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

同一个变量在不同的线程实例(ThreadLocal) 中可以有不同的值,可以通过Map.get(ThreadLocal)来获取。

既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。

每个Thread对象都持有一个ThreadLocalMap的实例,而ThreadLocal是维护ThreadLocalMap这个属性值的一个工具类。
Thread线程可以拥有多个ThreadLocal维护的自己线程独享的共享变量(这个共享变量只是针对自己线程里面共享)

ThreadLocal 的实现中,ThreadLocalMap 的键(key)被设计为弱引用,而值(value)是强引用,这种设计有其特定的原因和考虑:

1. 键作为弱引用的原因:

  • 自动清理:当 ThreadLocal 实例没有外部强引用时,它可能会被垃圾回收器回收。使用弱引用作为键可以确保当 ThreadLocal 没有被外部引用时,ThreadLocalMap 可以自动清理这些不再需要的键,从而避免内存泄漏。
  • 减少内存占用:如果使用强引用,即使 ThreadLocal 实例不再被使用,只要线程还在运行,ThreadLocalMap 中的条目就会一直存在,导致内存占用增加。

2. 值作为强引用的原因:

  • 访问速度ThreadLocal 的主要目的是快速地为每个线程提供局部变量。如果值是弱引用,那么每次访问时都需要检查引用的有效性,这会增加额外的开销,降低访问速度。
  • 确保数据完整性:使用强引用可以确保存储在 ThreadLocalMap 中的数据不会因为垃圾回收而意外丢失。这对于确保线程间数据隔离和数据安全非常重要。
    如果值也是弱引用,那么在没有外部引用的情况下,值可能会被垃圾回收器回收,这将导致线程无法访问到它所期望的局部变量值,从而造成程序逻辑错误。

3. 为什么这种设计是合理的:

  • 平衡内存管理和性能:通过将键设计为弱引用,可以在不需要手动管理内存的情况下自动清理不再使用的条目,从而减少内存泄漏的风险。同时,通过将值设计为强引用,可以确保数据的快速访问和完整性。
  • 符合使用场景:在多线程环境中,线程的生命周期通常比局部变量的生命周期要长。因此,即使 ThreadLocal 实例被回收,线程仍然可能继续运行并需要访问其局部变量。这种设计允许线程在不需要额外管理的情况下安全地使用局部变量。

4. 潜在问题及解决方案:

  • 内存泄漏:尽管键是弱引用,如果线程长时间运行且不结束,那么这个线程的 ThreadLocalMap 也会一直存在。由于 ThreadLocalMap 持有值的强引用,即使 ThreadLocal 已经被垃圾回收(键变成了 null),这些值仍然不会被自动回收,导致内存泄漏。
  • 为了避免这种情况,应该在不再需要 ThreadLocal 变量时,显式调用 remove() 方法来清理 ThreadLocalMap 中的条目。

总的来说,这种设计是在内存管理和性能之间做出的一种权衡,旨在提供一种既高效又相对安全的线程局部变量管理机制。

做了什么

ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。(有些变量不会产生实例副本)ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

总的来说,ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景

remove方法,直接将ThrealLocal 对应的值从当前相差Thread中的ThreadLocalMap中删除。为什么要删除,这涉及到内存泄露的问题。
public void remove() {
        ThreadLocalMap m = getMap(Thread.currentThread());
        if (m != null)
            m.remove(this);
    }

实际上 ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,弱引用的特点是,如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉。

所以如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候会被清理掉的,这样一来 ThreadLocalMap中使用这个 ThreadLocal 的 key 也会被清理掉。
但是,value 是强引用,不会被清理,这样一来就会出现 key 为 null 的 value。

ThreadLocal其实是与线程绑定的一个变量,如此就会出现一个问题:如果没有将ThreadLocal内的变量删除(remove)或替换,它的生命周期将会与线程共存。通常线程池中对线程管理都是采用线程复用的方法,在线程池中线程很难结束甚至于永远不会结束,这将意味着线程持续的时间将不可预测,甚至与JVM的生命周期一致。举个例字,如果ThreadLocal中直接或间接包装了集合类或复杂对象,每次在同一个ThreadLocal中取出对象后,再对内容做操作,那么内部的集合类和复杂对象所占用的空间可能会开始持续膨胀。

ThreadLocal与Synchronized的区别

ThreadLocal其实是与线程绑定的一个变量。ThreadLocal和Synchonized都用于解决多线程并发访问。

但是ThreadLocal与synchronized有本质的区别:

1、Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

2、Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本

,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。

而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
Synchronize可以实现悲观锁,ThreadLocal可以实现乐观锁

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值