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】详解多线程的概述及三种创建方法

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

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

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

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

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

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

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

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

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

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

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

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

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

    2024年02月10日
    浏览(52)
  • ThreadLocal 详解

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

    2023年04月08日
    浏览(31)
  • 【ThreadLocal详解】

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

    2024年02月12日
    浏览(38)
  • 史上最全ThreadLocal 详解(二)

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

    2024年02月02日
    浏览(56)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包