> 文档中心 > 【并发编程】结合实际需求说明ThreadLocal应用场景

【并发编程】结合实际需求说明ThreadLocal应用场景

在这里插入图片描述

一、如何“随时随地”获取线程局部变量?

最近有一个同事遇到这样一个需求:根据数据源配置信息不同,将数据批量入库到不同的数据库实例

  • 一共实现了AClass、BClass、CClass、DClass、ECalss这样五个类。
  • AClass中基于配置获取数据库数据源配置,然后初始化了一个JdbcTemplate
  • 将JdbcTemplate对象当作函数参数在5个类函数之间传递,最终在ECalss中使用JdbcTemplate将数据批量入库。

因此我提出了一个问题,你看人家Spring的那个Session想在哪获取就在哪获取,没有到处传参吧?

ServletRequestAttributes servletRequestAttributes  =(ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();HttpSession session = servletRequestAttributes.getRequest().getSession();

比如上面的这个代码在当前请求线程内,可以随时随地获取HttpSession,也不用把HttpSession当作函数参数到处传递啊?

二、方案一:ConcurrentHashMap

过了不一会同事给了我这样一种方案。使用全局静态Map存放JdbcTemplate,key为当前线程的线程名称(唯一性)。为了避免线程安全问题,同事使用了ConcurrentHashMap。

public static ConcurrentHashMap<String, JdbcTemplate> templates = new ConcurrentHashMap<>();

在这里插入图片描述

如上图所示,这个方案是可以实现需求的

  • ConcurrentHashMap templates作为一个静态成员变量,可以随时随地被引用。

  • 在AClass中根据数据源配置初始化JdbcTemplate,并将其放入Map。templates.put(Thread.currentThread().getName(),jdbcTemplate);

  • 用到jdbcTemplate的时候,随时从ConcurrentHashMap中取出来。templates.get(Thread.currentThread().getName())

虽然可以实现需求但是不够好,最主要的原因是:多线程使用一个Map,为了保证线程安全,ConcurrentHashMap使用了synchronized,所以执行效率肯定下降。就像多个人要上厕所,就一个坑,肯定有人需要等着。

三、方案二:使用ThreadLocal

所以更好的方案是:

public static ThreadLocal<JdbcTemplate> templates = new ThreadLocal<>();

ThreadLocal通过set方法将变量放入一个ThreadLocalMap,每个线程一份有效避免多线程资源竞争(如下图)。

  templates.set(jdbcTemplate);  //向ThreadLocalMap中放入  templates.get();  //从ThreadLocalMap中取出

在这里插入图片描述
ThreadLocalMap是当前线程Thread的一个属性(在执行ThreadLocal#set方法时被赋值),所以它的生命周期是和当前线程一样的,就不用我们自己去维护它的生命周期了,可以有效避免内存溢出问题。
在这里插入图片描述

当然如果你的线程一直执行不完,保存变量的ThreadLocalMap就一直不死,线程多了,数据大了,还是会内存溢出。

在这里插入图片描述