ThreadLocal 本地线程变量详解

这篇具有很好参考价值的文章主要介绍了ThreadLocal 本地线程变量详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

概述

ThreadLocal 意为本地线程变量,即该变量只属于当前线程,对其他线程隔离

我们知道,一个普通变量如果被多线程访问会存在存在线程安全问题,这时我们可以使用 Synchronize 来保证该变量某一时刻只能有一个线程访问,从而解决并发安全问题

但如果这个变量并不需要被共享,那么就可以使用 ThreadLocal 为每个线程提供一个完全独立的变量副本,每个线程只操作自身拥有的副本,彼此互不干扰

简而言之,Synchronized 用于线程间的数据共享,同步机制采用采用时间换空间的方式,而 ThreadLocal 则用于线程间的数据隔离,采用空间换时间的方式


ThreadLocal 使用

public class ThreadLocalTest {
  
  // 有个User对象需要在不同线程之间进行隔离访问,可以定义ThreadLocal如下
  static ThreadLocal<User> userThreadLocal = new ThreadLocal<>();

  // 设置线程本地变量的内容
  public void setUser(User user) {
    userThreadLocal.set(user);
  }

  public void getUser() {
    // 获取线程本地变量的内容
    User user = userThreadLocal.get();
    user.setUsername(user.getUsername + "-" + Thread.currentThread().getName());
    System.out.println(user.getUsername());
    // 移除线程本地变量
    userThreadLocal.remove();
  }
}

测试代码如下

public class DemoTest {
  public static void main(String[] args) {
    ThreadLocalTest threadLocalTest = new ThreadLocalTest();
    for(int i = 0; i < 100; i++) {
      Thread thread = new Thread(() -> {
        User user = new User();
        user.setUsername("小明");
        threadLocalTest.setUser(user);
        threadLocalTest.getUser();
      });
      thread.setName(String.valueOf(i));
      thread.start();
    }
  }
}

ThreadLocal 原理

首先,Java 中的线程是一个 Thread 类的实例对象,对象可以定义私有的成员变量,这也是 ThreadLocal 能实现线程本地变量的基础

在 Thread 类中定义了一个 Map 类型的成员变量,用来保存该线程的所有本地变量

ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocalMap 的 Entry 的定义如下,key 为 ThreadLocal 对象,v 就是我们要在线程之间隔离的对象

static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

ThreadLocal::set 方法的源码如下:

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        map.set(this, value);
    } else {
        createMap(t, value);
    }
}

使用 set 方法赋值时,首先会获取当前线程 thread,并获取 thread 线程的 ThreadLocalMap 属性。如果 map 属性不为空,则直接更新 value 值,key 就是自身的 ThreadLocal,如果 map 为空,则实例化 threadLocalMap,并将 value 值初始化

ThreadLocal::get 方法的源码如下:

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

使用 get 方法获取值,也是首先获取当前线程,再获取线程 ThreadLocalMap,如果 map 不为空就用自身 ThreadLocal 为 key 从 map 获取对应的 value

ThreadLocal::remove 方法的源码如下:

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null) {
        m.remove(this);
    }
}

remove 方法也是获取当前线程并获取 ThreadLocalMap,再将自身 ThreadLocal 为 key 对应的 value 移除

综合以上,我们知道 ThreadLocalMap 是线程的一个属性值,用来保存该线程的本地变量。ThreadLocal 能操作当前线程的 ThreadLocalMap,具体做法是以自身为 key 在 map 中存取值。因为每个线程的 ThreadLocalMap 都是独立的,所以每次使用 ThreadLocal 存取值都仅限于当前的线程,不会影响其他线程


ThreadLocal 内存泄露问题

内存泄露问题:指程序中动态分配的堆内存由于某种原因没有被释放或者无法释放,造成系统内存的浪费,导致程序运行速度减慢或者系统奔溃等严重后果。内存泄露堆积将会导致内存溢出

观察使用 ThreadLocal 时的内存布局

ThreadLocal 本地线程变量详解

当 ThreadLocal Ref 被回收了,由于在 Entry 使用的是强引用,在 Current Thread 还存在的情况下就存在着到达 Entry 的引用链,无法清除掉 ThreadLocal 的内容,同时 Entry 的 value 也同样会被保留,也就是说使用了强引用会出现内存泄露问题

为此,ThreadLocal 在 Entry 使用了弱引用

static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
        // Entry继承WeakReference,将key传入父级构造方法,从而形成弱引用
        super(k);
        value = v;
    }
}

再观察使用软引用后的内存布局

ThreadLocal 本地线程变量详解

当 ThreadLocal Ref 被回收,由于在 Entry 使用的是弱引用,因此在下次垃圾回收的时候就会将 ThreadLocal 对象清除

但由于 ThreadLocalMap 仍然存在 Current Thread Ref 这个强引用,Entry 中 value 的值仍然无法清除,还是存在内存泄露的问题,虽然当线程生命周期结束,Current Thread Ref 这个强引用也会随之消失,value 会在下一次垃圾回收被清除,但如果使用线程池,那么线程会被重新放回线程池等待复用,那么强引用就会一直存在。因此,我们在每次在使用完之后需要手动的 remove 掉 Entry 对象文章来源地址https://www.toymoban.com/news/detail-750664.html

到了这里,关于ThreadLocal 本地线程变量详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 【Java】详解多线程的概述及三种创建方法

    【Java】详解多线程的概述及三种创建方法

    🌺 个人主页 : Dawn黎明开始 🎀 系列专栏 : Java ⭐ 每日一句 : 身在井隅,心向阳光,眼里有诗,自在远方 📢 欢迎大家:关注 🔍+ 点赞 👍+评论 📝+ 收藏⭐️ 文章目录 一.🔐多线程 📋前言 1.1🔓进程 1.1.1🔑什么是进程? 1.1.2🔑多进程有什么意义呢? 1.2🔓线程 1.2.1🔑什

    2024年02月05日
    浏览(11)
  • 线程安全—ThreadLocal

    定义: ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为每个线程都创建了一个副本,存的是自己,那么每个线程可以访问自己内部的副本变量。 ThreadLocal是用空间换取时间,synchronized是用时间换空间。

    2024年02月11日
    浏览(10)
  • ThreadLocal线程安全示例及其原理

    ThreadLocal线程安全示例及其原理

    提示:多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他操作,调用这个对象的行为都可以获得正确的结果,那么这个对象就是线程安全的 ThreadLocal是用空间换取时间,synchronized关键

    2024年02月07日
    浏览(10)
  • 【Java】线程数据共享和安全 -ThreadLocal

    【Java】线程数据共享和安全 -ThreadLocal

     🎄欢迎来到@边境矢梦°的csdn博文🎄  🎄本文主要梳理线程数据共享和安全 -ThreadLocal🎄 🌈我是边境矢梦°,一个正在为秋招和算法竞赛做准备的学生🌈 🎆喜欢的朋友可以关注一下 🫰🫰🫰 ,下次更新不迷路🎆 Ps: 月亮越亮说明知识点越重要 (重要性或者难度越大)🌑🌒

    2024年02月09日
    浏览(28)
  • ThreadLocal加切面实现线程级别的方法缓存

    当一个请求线程多次请求A方法时,只会触发一次A方法的实际调用,会将方法结果缓存起来,避免多次调用。 1. 需要一个注解ThreadLocalCache,在需要缓存的方法上加上该注解 2. 需要一个切面,借助ThreadLocal,将结果缓存起来,利用环绕通知来实现方法拦截从缓存中返回方法执行结果 3.1、

    2024年04月10日
    浏览(12)
  • (线程池)多线程使用场景--es数据批量导入、数据汇总、异步调用;如何控制某个方法允许并发访问线程的数量;对ThreadLocal的理解及实现原理、源码解析、ThreadLocal的内存泄露问题

    (线程池)多线程使用场景--es数据批量导入、数据汇总、异步调用;如何控制某个方法允许并发访问线程的数量;对ThreadLocal的理解及实现原理、源码解析、ThreadLocal的内存泄露问题

    CountDownLatch(闭锁/倒计时锁) 用来进行线程同步协作,等待所有线程完成倒计时(一个或者多个线程,等待其他多个线程完成某件事情之后才能执行) 其中构造参数用来初始化等待计数值 await() 用来等待计数归零 countDown() 用来让计数 减一 多线程使用场景一:( es数据批量导

    2024年04月25日
    浏览(14)
  • java八股文面试[多线程]——ThreadLocal底层原理和使用场景

    java八股文面试[多线程]——ThreadLocal底层原理和使用场景

    源码分析: ThreadLocal中定义了ThreadLocalMap静态内部类,该内部类中又定义了Entry内部类。 ThreadLocalMap定了 Entry数组。 Set方法: Get方法: Thread中定义了两个ThreaLocalMap成员变量: Spring使用ThreadLocal解决线程安全问题  我们知道在一般情况下,只有 无状态的Bean 才可以在多线程环

    2024年02月10日
    浏览(12)
  • 【ThreadLocal详解】

    【ThreadLocal详解】

      ThreadLocal是一个用于实现线程数据隔离的一个类,每个线程访问时,通过Get、Set方法都会产生一个属于该线程的局部变量副本,当线程结束时,ThreadLocal及变量随着线程一起被回收。 总的来说,ThreadLocal有三大用途:   ThreadLocal虽然叫线程局部变量,但是它不存储任何数据

    2024年02月12日
    浏览(9)
  • ThreadLocal 详解

    ThreadLocal 详解

    ThreadLocal类用来提供线程内部的局部变量,不同的线程之间不会相互干扰 这种变量在多线程环境下访问(通过get和set方法访问)时能保证各个线程的变量相对独立于其他线程内的变量 在线程的生命周期内起作用,可以减少同一个线程内多个函数或组件之间一些公共变量传递的

    2023年04月08日
    浏览(4)
  • 史上最全ThreadLocal 详解(二)

    史上最全ThreadLocal 详解(二)

    ThreadLocal 内存泄露的原因及处理方式 目录 1、ThreadLocal 使用原理 2、ThreadLocal 内存泄露的原因 3、 为什么不将key设置为强引用 3.1 、key 如果是强引用 3.2、key 如果是强引用 3.3  那么为什么 key 要用弱引用 3.4 如何正确的使用ThreadLocal        前文我们讲过ThreadLocal的主要用途是

    2024年02月02日
    浏览(10)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包