真实项目中 ThreadLocal 的妙用
一、什么是 ThreadLocal
ThreadLocal 提供了线程的局部变量,每个线程都可以通过 set() 和 get() 来对这个局部变量进行操作,但不会和其他线程的局部变量冲突,实现了线程间的据隔离。
简单讲:一个获取用户的请求线程 A,如果向 ThreadLocal 填充变量 AValue(只能被线程 A 操作),该变量对其他获取用户的请求线程 B、C...是隔离的.
最简单的使用方式:
类似一次 HTTP 请求线程中,利用 ThreadLocal 存储 Cookie 对象,进行状态管理。set Cookie:
private ThreadLocal httpThreadLocal = new ThreadLocal();
httpThreadLocal.set(“Cookie: sid=13420771402233”)
上面存储格式是 String ,实际场景存储的是具体的对象。在这次 HTTP 请求过程中,任何时候都可以获取 Cookie 。获取方式很简单 get Cookie:
String cookieValue = (String) httpThreadLocal.get();
Thread 与 ThreadLocal 对象引用关系图
二、你熟悉的场景
2.1 数据库连接池
比如一次请求线程进来,业务 Dao 需要更新 user 表和 user-detail 表。如果是 new 出两个数据库 Connection ,分别不同的 Connection 操作 user 表和 user-detail 表,就无法保证事务。那么数据库连接池是如何保证的?
答案是:利用 ThreadLocal 存储唯一 Connection 对象。每次请求线程,pool.getConnection 获取连接的时候都会这样操作:
- 会从 ThreadLocal 获取 Connection 对象。如果有,则保证了后面多个数据库操作共用同一个 Connection ,从而保证了事务。
- 如果没有,往 ThreadLocal 新增Connection 对象,并返回到线程
错误的做法
public class XXXService {
private Connection conn;
}
因为 conn 是线程不安全的。这样会导致多个请求公用一个连接。请求量很大的情况下,延迟各种。你懂。
因此,使用 ThreadLocal 保证每个请求线程的 Connection 是唯一的。即每个线程有自己的连接。
继续讲到 Spring 框架,在事务开始时,会给当前线程一个Jdbc Connection,在整个事务过程,都是使用该线程绑定的connection来执行数据库操作,实现了事务的隔离性。Spring框架里面就是用的ThreadLocal来实现这种隔离
2.2 HTTP Cookie
比如你访问百度、我访问百度,会有不同 Cookie 。而且你不能访问我的 Cookie,我也不能。顾名思义,使用 ThreadLocal 保证每个 HTTP 请求线程的 Cookie 是唯一的。
Cookie 这样才能做 Session 等状态管理。
三、实战场景
总结一下就是:ThreadLocal 可以让同一个线程中上下文之间数据共享
在上面章节 二、你熟悉的场景 其实介绍了很多现有场景。那么我这边具体的实战场景是什么?
简单的例子:
适用满足这两个条件的场景:1.每个线程独有的一些信息,2.这些信息又会在多个方法或类中用到。
- 一个请求线程,里面有两个异步小线程,各有一个方法。分别处理 A 或 B 业务
- 一种方法是传递不可变的入参
- 另一种就是 ThreadLocal,放在 ThreadLocal 的入参,会被各个方法共享。而且多个请求线程互不影响
复杂的例子:
一次发货操作:会根据入参,进行组件化、流程编排话。那么入参会被各个地方用到,而且有些流程组件是异步的(类似 new thread 操作的)。这时候可以定一个 XXContext 上下文:
public class XXContext {
private static ThreadLocal<Map<Class<?>, Object>> context = new InheritableThreadLocal<>();
/**
* 把参数设置到上下文的Map中
*/
public static void put(Object obj) {
Map<Class<?>, Object> map = context.get();
if (map == null) {
map = new HashMap<>();
context.set(map);
}
if (obj instanceof Enum) {
map.put(obj.getClass().getSuperclass(), obj);
} else {
map.put(obj.getClass(), obj);
}
}
/**
* 从上下文中,根据类名取出参数
*/
@SuppressWarnings("unchecked")
public static <T> T get(Class<T> c) {
Map<Class<?>, Object> map = context.get();
if (map == null) {
return null;
}
return (T) map.get(c);
}
/**
* 清空ThreadLocal的数据
*/
public static void clean() {
context.remove();
}
}
代码解析:
- 都是 static 操作,类似 DateUtil 玩法
- 记得每次请求线程后清理。可以 AOP 去清理,加个注解就行。因为同一个请求线程可能被业务方公用。
(完)

真实项目中 ThreadLocal 的妙用的更多相关文章
- Java算法之递归打破及在真实项目中的使用实例
开心一笑 刚才领导问开发:"你觉得这个项目的最大风险是什么",开发说:"加班猝死" , 气氛尴尬了一分钟!!! 提出问题 1.递归算法简单复习 2.如何实现递归 ...
- memcached真实项目中的应用
上一篇memcached介绍及基本使用介绍了memcached的一些基本概念和一个范例.这一篇将介绍一个memcached在实际项目中的应用
- 【一起学设计模式】观察者模式实战:真实项目中屡试不爽的瓜娃EventBus到底如何实现观察者模式的?
申明 本文章首发自本人公众号:壹枝花算不算浪漫,如若转载请标明来源! 感兴趣的小伙伴可关注个人公众号:壹枝花算不算浪漫 22.jpg 前言 之前出过一个设计模式的系列文章,这些文章和其他讲设计模式的文 ...
- 真实项目中VS2015中自建T4模板生成文件的使用
有可能许多小伙伴们发现,vs2015和2012的自带T4模板中的.tt文件改变非常之多,如果仅仅copyEF系统自己生成的模板文件,那可累了.以下是我自己整理的在2012和2015中都可以试用的代码. ...
- java使用elasticsearch进行模糊查询-已在项目中实际应用
java使用elasticsearch进行模糊查询 使用环境上篇文章本人已书写过,需要maven坐标,ES连接工具类的请看上一篇文章,以下是内容是笔者在真实项目中运用总结而产生,并写的是主要方法和思路 ...
- Web项目中得到访问者的真实ip
Web项目中得到访问者的真实ip 描述:最近要实现个功能是要记录管理员登录的真实ip,但在项目中如果直接使用request.getRemoteAddr()获得ip的话,获得的可能不是真实ip,是因为使 ...
- 编写Java程序,使用ThreadLocal类,项目中创建账户类 Account,类中包括账户名称name、 ThreadLocal 类的引用变量amount,表示存款
查看本章节 查看作业目录 需求说明: 某用户共有两张银行卡,账户名称相同,但卡号和余额不同.模拟用户使用这两张银行卡进行消费的过程,并打印出消费明细 实现思路: 项目中创建账户类 Account,类中 ...
- JAVA WEB项目中各种路径的获取
JAVA WEB项目中各种路径的获取 标签: java webpath文件路径 2014-02-14 15:04 1746人阅读 评论(0) 收藏 举报 分类: JAVA开发(41) 1.可以在s ...
- 转:C++项目中的extern "C" {}
引言 在用C++的项目源码中,经常会不可避免的会看到下面的代码: #ifdef __cplusplus extern "C" { #endif /*...*/ #ifdef __c ...
随机推荐
- WPF 通过CommandBinding捕获命令
RoutedCommand与业务逻辑无关,业务逻辑是通过CommandBinding来实现 using System; using System.Collections.Generic;using S ...
- JavaScript严格模式分析
简要:严格模式(strict mode)是JavaScript在ES5里面新增的编码模式,只要一行代码 就可开启,可谓 非常简单了,而它对于 我们的编码来说到底有什么不同呢? 一. 严格模式的目的? ...
- iOS NSString追加字符串的方法
第一种: NSArray *array = [NSArray arrayWithObjects:@"Hello",@" ",@"world" ...
- 使用Netconf管理Cisco网络设备
测试环境:Cisco CSR1000V虚拟化环境 Step 1:开启Cisco设备netconf-Yang,如下图: CSR1000V(config)#netconf-yang CSR1000V(co ...
- .NET DataTable转换为JSON格式的字符串
在进行数据传递的时候,有时我们需要通过Ajax的方式或者其他的方式传递一个数据列表,可以将DataTable或者其他形式的数据列表转换为JSON的格式,通过Ajax实体的形式进行传递. 比如说: // ...
- Linux下的软件安装
在线安装 APT:advanced packaging Tool,Debian及其派生的发行版的软件包管理工具,包含以apt-开头的多个工具,如apt-get,apt-cache,apt-cdrom ...
- MinGW gcc 生成动态链接库 dll 的一些问题汇总(由浅入深,很详细)
网络上关于用 MinGW gcc 生成动态链接库的文章很多.介绍的方法也都略有不同.这次我在一个项目上刚好需要用到,所以就花了点时间将网上介绍的各种方法都实验了一遍.另外,还根据自己的理解试验了些网上 ...
- virtualbox ubuntu16.04 自动挂载共享文件夹
为了操作方便,需要ubuntu 在开机运行时自动挂载共享文件夹,ubuntu的版本是16.04,宿主机是win10,步骤如下: 1. 在virtualbox “设备”-“共享文件夹”中设置共享文件夹如 ...
- 《HTML开发Mac OS App 视频教程》 第001讲、入门教程
土豆网同步更新:http://www.tudou.com/plcover/VHNh6ZopQ4E/ 使用HTML 创建Mac OS App 视频教程. 官方QQ群: (1)App实践出真知 434 ...
- 在VS如何查看汇编代码
由于最近不常用,结果导致今天用的时候忘记了,╮(╯▽╰)╭.现在标记一下: 方法如下,先创建一个C++ Project,然后加入上面的代码,在main函数或者其他地方设置断点,注意是Debug版本,否 ...