侧边栏壁纸
博主头像
gale-blog博主等级

少年一贯快马扬帆,道阻且长不转弯,要盛大,要绚烂,要哗然,要用理想的泰坦尼克号去撞现实冰川,要当烧赤壁的风,而非借箭草船,要为了一片海,就肯翻万山

  • 累计撰写 39 篇文章
  • 累计创建 5 个标签
  • 累计收到 5 条评论

目 录CONTENT

文章目录

java开发之多线程ThreadLocal

二月在这里
2024-02-21 / 0 评论 / 0 点赞 / 41 阅读 / 4535 字

ThreadLocal 是 Java 提供的一个线程内部的存储类,可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。

特点:

  • ThreadLocal 中存储的是线程隔离的数据,该数据只能被当前线程访问,其他线程无法访问和修改。

  • 每个 ThreadLocal 对象只能被当前线程使用,其他线程无法访问。

  • 每个线程中都可以独立地改变自己的 ThreadLocal 变量,不会影响其他线程的变量。

  • 当线程结束时,ThreadLocal 中存储的对象会被垃圾回收。

实现原理:

  • ThreadLocal 是一个映射表,Key 是 ThreadLocal 实例本身,Value 是真正要存储的对象。每个线程都有一个独立的 ThreadLocalMap 副本。

  • 每个 Thread 线程内部都有一个 ThreadLocalMap 的引用,这个映射表是用于存储实际的对象或值的。

  • 当调用 ThreadLocal 的 set() 方法设置值的时候,先得到当前线程的 ThreadLocalMap,然后将 ThreadLocal 实例本身作为键,而要存储的对象作为值放入 ThreadLocalMap。

  • 当调用 get() 方法获取值时,同样先获取当前线程的 ThreadLocalMap,然后以 ThreadLocal 实例作为键,从 ThreadLocalMap 获取对应的对象。

  • 因为每个线程的 ThreadLocalMap 是隔离的,所以一个线程无法访问其他线程中的值,从而实现了线程隔离。

  • 当线程结束时,线程的 ThreadLocalMap 也会随之销毁,避免了内存泄漏。

  • ThreadLocalMap 使用 ThreadLocal 的弱引用作为 key,如果一个 ThreadLocal 没有外部强引用来引用它,那么系统 GC 的时候,这个 ThreadLocal 会被回收,这样就避免了 ThreadLocal 本身的内存泄漏。

使用场景:

  • 频繁创建和销毁的对象,可以避免创建开销。

  • 每个线程需要单独的实例,通常用于代理,事务等。

  • 需要将共享资源的访问限制在同一个线程内,避免多线程访问导致竞争。

  • 存储线程上下文数据,如用户身份信息等。

内存泄露可能原因

  • 孤儿引用对象无法被GCThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用引用它,那么系统GC的时候,这个ThreadLocal势必会被回收,而它对应的value却有可能无法被回收,因为当前线程的ThreadLocalMap还持有该value的强引用,导致value无法被回收,出现内存泄漏。

  • 线程生命周期过长如果线程生命周期过长,那么这些线程持有的ThreadLocalMap也不会被回收,即使ThreadLocal已经被回收,但ThreadLocalMap还在,value也无法被回收。这种情况下可能需要手动调用remove()方法清除数据。

解决内存泄露方法

  • 每次使用完ThreadLocal,都调用它的remove()方法,清除数据值。

  • 使用完线程后,确保线程销毁,不要使线程生命周期过长。

  • 不需要维护的ThreadLocal及时设置为null,让它可被回收。

  • 用try-finally块包装线程执行体,在finally块中清除ThreadLocal。

示例

public class ThreadLocalExample {
​
  // 声明ThreadLocal变量
  private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();
​
  public static void main(String[] args) {
      // 在主线程中设置threadLocal的值
      threadLocal.set("Main Thread");
       
      // 在子线程中获取和设置threadLocal的值
      new Thread(() -> {
          System.out.println(threadLocal.get()); // 输出null
          threadLocal.set("Child Thread");
          System.out.println(threadLocal.get()); // 输出Child Thread
      }).start();
       
      // 在主线程中再次打印threadLocal的值
      System.out.println(threadLocal.get()); // 输出Main Thread
  }
}

0

评论区