还能这样读ThreadLocal?

简介: ThreadLocal是Java中的一个类,用于创建线程本地变量,即每个线程都有自己的变量副本,互不干扰。它能够提供了线程的局部变量,让每个线程都可以通过set/get来对这个局部变量进行操作。不会和其他线程的局部变量进行冲突,实现了线程的数据隔离。

其他系列文章导航

Java基础合集

设计模式合集

多线程合集

分布式合集

ES合集


文章目录

其他系列文章导航

文章目录

前言

一、Threadlocal通俗理解

1.1定义

1.2场景

1.2.1日期时间格式化工具类

1.2.2Spring事务

1.3原理

二、内存泄漏


前言

ThreadLocal是Java中的一个类,用于创建线程本地变量,即每个线程都有自己的变量副本,互不干扰。


一、Threadlocal通俗理解

1.1定义

它能够提供了线程的局部变量,让每个线程都可以通过set/get来对这个局部变量进行操作。

不会和其他线程的局部变量进行冲突,实现了线程的数据隔离。

1.2场景

1.2.1日期时间格式化工具类

格式化/转化的实现是用的SimpleDateFormat。

但众所周知SimpleDateFormat不是线程安全的,所以我们就用ThreadLocal来让每个线程装载着自己的SimpleDateFormat对象,以达到在格式化时间时,线程安全的目的。

在方法上创建SimpleDateFormat对象也没问题,但每调用一次就创建一次有点不优雅。

1.2.2Spring事务

Spring提供了事务相关的操作,而我们知道事务是得保证一组操作同时成功或失败的。

这意味着我们一次事务的所有操作需要在同一个数据库连接上。

但是在我们日常写代码的时候是不需要关注这点的。

Spring就是用的ThreadLocal来实现,ThreadLocal存储的类型是一个Map 。

Map中的key 是DataSource,value 是Connection (为了应对多数据源的情况所以是一个Map)。

用了ThreadLocal保证了同一个线程获取一个Connection对象,从而保证一次事务的所有操作需要在同一个数据库连接上。

1.3原理

ThreadLocal是一个壳子,真正的存储结构是ThreadLocal里有ThreadLocalMap这么个内部类

而有趣的是,ThreadLocalMap的引用是在Thread上定义的。

ThreadLocal本身并不存储值,它只是作为key来让线程从ThreadLocalMap获取value。

所以,得出的结论就是ThreadLocalMap该结构本身就在Thread下定义,而ThreadLocal只是作为key,存储set到ThreadLocalMap的变量当然是线程私有的咯。

疑问:

我可以在ThreadLocal下定义Map,key是Thread,value是set进去的值吗?
就是说,为啥我要把ThreadLocal做为key,而不是Thread做为key?这样不是更清晰吗?

答案:

实际上就是所有的线程都访问ThreadLocal的Map,而key是当前线程。
但这有点小问题,一个线程是可以拥有多个私有变量的嘛,那key如果是当前线程的话,意味着还点做点 [手脚],来唯一标识set进去的value 。

还有个问题就是:并发量足够大时,意味着所有的线程都去操作同一个Map,Map体积有可能会膨胀,导致访问性能的下降。
并且这个Map维护着所有的线程的私有变量,意味着你不知道什么时候可以[销毁]。

现在JDK实现的结构就不一样了。
线程需要多个私有变量,那有多个ThreadLocal对象足以,对应的Map体积不会太大。

只要线程销毁了,ThreadLocalMap也会被销毁品。


二、内存泄漏

在使用ThreadLocal时,如果没有及时清理ThreadLocal变量,就会导致内存泄漏问题。

讲内存泄漏问题前,先科普一下Java中有四种引用类型,分别为:

    1. 强引用 (Strong Reference):被强引用关联的对象不会被垃圾回收器回收,只有在该引用被显式地赋值为null后,对象才会被回收。
    2. 软引用 (Soft Reference):被软引用关联的对象只有在内存不足时才会被垃圾回收器回收。
    3. 弱引用 (Weak Reference):被弱引用关联的对象在下一次垃圾回收时一定会被回收。
    4. 虚引用 (Phantom Reference):被虚引用关联的对象在任何时候都可能被垃圾回收器回收。虚引用通常用于跟踪对象被垃圾回收的状态,可以在对象被回收时收到一个系统通知。

    ThreadLocal的内部实现是将每个线程维护的变量存储在一个Map中,线程的ThreadLocal变量作为Map的key,而变量值则作为Map的value。当线程结束时,ThreadLocal变量不会被垃圾回收器回收,因为它们仍然被Map所引用。

    如果不主动清理ThreadLocal变量,就会导致Map中的所有对象都无法被垃圾回收,从而引起内存泄漏。一般来说,需要在ThreadLocal使用完毕后及时地调用remove方法将其删除。

    以下是一个示例代码,演示了如何使用ThreadLocal解决线程安全问题,并展示了如何避免内存泄漏:

    public class MyService {
        private static ThreadLocal<Object> threadLocal = new ThreadLocal<>();
        public void setValue(Object value) {
            threadLocal.set(value);
        }
        public Object getValue() {
            return threadLocal.get();
        }
        public void removeValue() {
            threadLocal.remove();
        }
    }

    image.gif

    在使用ThreadLocal时,需要注意以下几点:

      1. 尽量避免创建过多的ThreadLocal变量,因为每个ThreadLocal都需要占用一定的内存。
      2. 在ThreadLocal使用完毕后,必须调用remove方法将其删除,否则会引起内存泄漏。
      3. 在使用ThreadLocal时,建议使用try-finally语句块,确保即使出现异常,也能够及时地清理ThreadLocal变量,避免内存泄漏问题。

      疑问1:

      为什么要将ThreadLocalMao的key设置为弱引用呢?强引用不香吗?

      回答:

      外界是通过ThreadLocal来对ThreadLocalMap进行操作的,假设外界使用ThreadLocal的对象被置null了,那ThreadLocalMap的强引用指向ThreadLocal也毫无意义啊。

      弱引用反而可以预防大多数内存泄漏的情况。
      毕竟被回收后,下一次调用set/get/remove时ThreadLocal内部会清除掉。

      疑问2:

      建议把ThreadLocal修饰为static,为什么?

      回答:

      ThreadLocal能实现了线程的数据隔离不在于它自己本身,而在于Thread的Thr
      eadLocalMap。

      所以,ThreadLocal可以只初始化一次只分配一块存储空间就足以了,没必要作为成员变量多次被初始化。

      目录
      相关文章
      |
      3月前
      |
      存储 Java 测试技术
      ThreadLocal:线程专属的变量
      ThreadLocal:线程专属的变量
      51 0
      |
      7天前
      |
      存储 设计模式 安全
      深入理解ThreadLocal原理
      本文深入探讨了Java中的ThreadLocal及其内部数据结构ThreadLocalMap的工作原理和特性,帮助读者理解如何利用ThreadLocal实现线程局部变量的隔离和线程安全。
      深入理解ThreadLocal原理
      |
      21天前
      |
      存储 SQL Java
      (七)全面剖析Java并发编程之线程变量副本ThreadLocal原理分析
      在之前的文章:彻底理解Java并发编程之Synchronized关键字实现原理剖析中我们曾初次谈到线程安全问题引发的"三要素":多线程、共享资源/临界资源、非原子性操作,简而言之:在同一时刻,多条线程同时对临界资源进行非原子性操作则有可能产生线程安全问题。
      |
      26天前
      |
      存储 安全 Java
      多线程线程安全问题之ThreadLocal是什么,它通常用于什么场景
      多线程线程安全问题之ThreadLocal是什么,它通常用于什么场景
      |
      3月前
      |
      存储 安全 Java
      ThreadLocal原理讲解
      ThreadLocal原理讲解
      39 0
      |
      3月前
      |
      存储 安全 Java
      【线程本地变量ThreadLocal】—— 每天一点小知识
      【线程本地变量ThreadLocal】—— 每天一点小知识
      |
      存储 Java 数据库连接
      【并发技术07】使用ThreadLocal在线程范围内共享数据
      【并发技术07】使用ThreadLocal在线程范围内共享数据
      |
      存储 算法 安全
      ThreadLocal原理剖析
      ThreadLocal原理剖析
      186 0
      |
      Java 定位技术
      ThreadLocal原理
      经典八股文之ThreadLocal原理
      172 0
      |
      存储 缓存 Java
      线程本地存储 ThreadLocal
      本文主要介绍了 ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal
      526 2
      线程本地存储 ThreadLocal