对于 ThreadLocal 我们都不陌生,它的作用如同它的名字——用于存放「线程本地」变量。

先通过一个小例子感受一下:

private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) throws Throwable {
    Thread threadOne = new Thread(()->{
        threadLocal.set("ThreadOne:"   Thread.currentThread().getName());
        log.info("线程 One 本地变量值为:{}", threadLocal.get());
        threadLocal.remove();
        log.info("线程 One remove 后本地变量值为:{}", threadLocal.get());
    });
    Thread threadTwo = new Thread(()->{
        threadLocal.set("ThreadTwo:"   Thread.currentThread().getName());
        log.info("线程 Two 本地变量值为:{}", threadLocal.get());
    });
    threadOne.start();
    threadTwo.start();
}

运行结果:

线程 One 本地变量值为:ThreadOne:Thread-0
线程 One remove 后本地变量值为:null
线程 Two 本地变量值为:ThreadTwo:Thread-1

OK,从效果上看,ThreadLocal 确实是线程隔离的,那么,它是如何做到线程隔离的呢?下面我们扒一扒源码,看看它是如何做到的:

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() 方法的逻辑如下:

  • 获取当前线程
  • 根据当前线程获取一个 ThreadLocalMap 对象
  • 如果 map 不为 null 则保存
  • 如果 map 为 null 则创建一个 map

getMap() 和 createMap() 方法都干了啥呢?我们点进去看:

ThreadLocalMap getMap(Thread t) {
   return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
   t.threadLocals = new ThreadLocalMap(this, firstValue);
}

进入到两个方法内部后发现,不管执行哪个分支,最终是把值保存到了当前线程的 threadLocals 属性中。

查看 Thread 类的源码,你会发现类中定义了一个 threadLocals 属性,且初始值为 null,其类型为ThreadLocal.ThreadLocalMap。

public class Thread implements Runnable {
// ...
   ThreadLocal.ThreadLocalMap threadLocals = null;
// ...
}

到此,我们发现了,原来 ThreadLocal 就是把我们要传递的对象放到了当前线程的 threadLocals 属性中。也就是说每个线程在用 ThreadLocal 保存对象时,其实就是将对象放到了当前线程实例对象的 threadLocals 属性里面。这样一来线程之间自然就是互相独立的啦。

再看看 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();
}

private T setInitialValue() {
   T value = initialValue();
   Thread t = Thread.currentThread();
   ThreadLocalMap map = getMap(t);
   if (map != null)
       map.set(this, value);
   else
       createMap(t, value);
   return value;
}

ThreadLocal 的 get() 方法其实和 set() 方法逻辑很相似,先从当前线程的 threadLocals 属性中取,如果该属性为 null,那么就初始化。

当线程结束时,会调用当前线程实例的 exit() 方法,将 threadLocals 设置为 null,以便垃圾回收器将其回收掉。

// THread 类中的方法
private void exit() {
   // ...
   threadLocals = null;
   // ...
}

最后,有一点需要格外注意:用完 ThreadLocal 一定要记得手动调用 remove() 方法,否则可能会产生脏数据甚至产生内存泄漏。

为啥呢?上面不是说线程结束时,会将 threadLocals 置为 null 吗?

是的,线程结束时,确实会做清理工作。

但,如果线程一直不结束呢?如果线程会被复用呢?比如使用了线程池。

所以,使用 ThreadLocal 一定要手动 remove()。​

补充:下面看下ThreadLocal 是什么?有哪些使用场景?

ThreadLocal是一个本地线程副本变量工具类,主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰。

说人话就是,ThreadLocal在每个线程都创建副本,每个线程可以访问自己的副本,线程之间相互不影响。

使用场景:

多线程环境中为每一个jdbc分配一个Connection连接,使用ThreadLocal去保存连接,这样就保证了每个线程在自己的连接上操作数据库,不会出现A线程关闭B的Connnection操作

Web中的session管理时,可以使用ThreadLocal记录每个线程的Session,这样保证每个线程都可以获取自己的session

解决线程安全问题,对于需要进行线程隔离的变量,可以使用ThreadLocal存储,确保线程隔离

总结:

ThreadLocal可以简单看作一个map,每个线程保存一个map,这个map只能存储一组数据,key为线程id, value就是要存的值

一个ThreadLocal只能存储一个变量,如果要存多个就创建多个ThreadLocal对象。

到此这篇关于为什么 ThreadLocal 可以做到线程隔离?的文章就介绍到这了,更多相关ThreadLocal 线程隔离内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

告诉你为什么 ThreadLocal 可以做到线程隔离的更多相关文章

  1. 告诉你为什么 ThreadLocal 可以做到线程隔离

    对于 ThreadLocal 我们都不陌生,它的作用如同它的名字用于存放线程本地变量,这篇文章主要介绍了为什么 ThreadLocal 可以做到线程隔离,需要的朋友可以参考下

  2. 利用ThreadLocal实现一个上下文管理组件

    本文基于ThreadLocal原理,实现了一个上下文状态管理组件Scope,通过开启一个自定义的Scope,在Scope范围内,可以通过Scope各个方法读写数据,感兴趣的可以了解一下

  3. Java中ThreadLocal线程变量的实现原理

    本文主要介绍了Java中ThreadLocal线程变量的实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  4. Java中ThreadLocal 导致内存 OOM 的原因分析

    这篇文章主要介绍了Java中ThreadLocal导致内存OOM的原因分析,文章基于Java的相关内容展开ThreadLocal导致内存OOM的原因分析,需要的小伙v阿布可以参考一下

  5. Java ThreadLocal类使用详解

    这篇文章主要介绍了Java ThreadLocal类详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下

  6. 深入理解Java并发编程之ThreadLocal

    本文主要介绍了Java并发编程之ThreadLocal,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  7. Java线程变量ThreadLocal源码分析

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

  8. Java自定义过滤器和拦截器实现ThreadLocal线程封闭

    本文主要介绍了Java自定义过滤器和拦截器实现ThreadLocal线程封闭,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  9. ThreadLocal数据存储结构原理解析

    这篇文章主要为大家介绍了ThreadLocal数据存储结构原理解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. SpringBoot ThreadLocal实现公共字段自动填充案例讲解

    每一次在Controller层中封装改动数据的方法时都要重新设置一些共性字段,显得十分冗余。为了解决此问题也是在项目中第一次利用到线程,总的来说还是让我眼前一亮,也开阔了视野,对以后的开发具有深远的意义

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部