真实项目中 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来实现这种隔离

比如你访问百度、我访问百度,会有不同 Cookie 。而且你不能访问我的 Cookie,我也不能。顾名思义,使用 ThreadLocal 保证每个 HTTP 请求线程的 Cookie 是唯一的。

Cookie 这样才能做 Session 等状态管理。

三、实战场景

总结一下就是:ThreadLocal 可以让同一个线程中上下文之间数据共享

在上面章节 二、你熟悉的场景 其实介绍了很多现有场景。那么我这边具体的实战场景是什么?

简单的例子:

适用满足这两个条件的场景:1.每个线程独有的一些信息,2.这些信息又会在多个方法或类中用到。

  1. 一个请求线程,里面有两个异步小线程,各有一个方法。分别处理 A 或 B 业务
  2. 一种方法是传递不可变的入参
  3. 另一种就是 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 去清理,加个注解就行。因为同一个请求线程可能被业务方公用。

(完)

file

转载于:https://www.cnblogs.com/Alandre/p/11145516.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/247626.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

css之flex布局

flex布局是css3中的重要布局方式&#xff0c;称为“弹性布局”&#xff0c;每次想到它主要是遇到元素垂直居中、元素宽高自适应的问题&#xff0c;这些问题在flex中都能过简单设置就解决&#xff0c;它更像是原生APP中的布局操作&#xff0c;布局不必写N多的盒模型代码来实现&a…

javascript对URL中的参数进行简单加密处理

javascript的api本来就支持Base64&#xff0c;因此我们可以很方便的来进行编码和解码。 var encodeData window.btoa("namexiaoming&age10")//编码 var decodeData window.atob(encodeData)//解码。 下面来个具体的例子来说明如何对url中参数进行转码&#xff…

Fibinary Numbers

http://acm.hust.edu.cn/vjudge/contest/view.action?cid30506#problem/V 题意&#xff1a;从右向左&#xff0c;每一个位数&#xff0c;分别表示一个fibonacci数&#xff0c;1表示有&#xff0c;0表示没有&#xff1b;求两个数的和&#xff0c;同样按照这种形式存储 #include…

移动web开发DRP问题

DPR dpr问题是移动端web开发上需要注意的问题&#xff0c;用大白话说就是&#xff0c;代码中所设置的像素值实际上是虚拟像素&#xff0c;手机屏幕上的像素实际为物理像素&#xff0c;原始的手机&#xff0c;虚拟像素与物理像素是1:1覆盖的&#xff0c;但随着移动端屏幕的技术发…

HTML元素title里面如何换行

在调试代码的时候我就遇到一个问题&#xff0c;HTML元素title里面通常只显示一行&#xff0c;那我想要他换行&#xff0c;就是多行显示&#xff0c;如何实现&#xff1f;JS代码里面比如Alert里面又该如何换行&#xff1f; 经过我的一番实验 要实现这种效果有几种方法&#xff0…

A20 GPIO中断类型差别结果迥异的问题思考

A20GPIO中断类型差别结果迥异的问题思考 最近在使用全志A20做开发时&#xff0c;发现在处理中断的时候&#xff0c;用电平触发模式&#xff0c;报中断比较乱&#xff0c;用边沿触发则很稳定&#xff0c;不会乱报。笔者感到比较困惑&#xff0c;笔者用电平触发写的code如下&…

div内图片和文字水平垂直居中

大小不固定的图片、多行文字的水平垂直居中 本文综述 想必写css的都知道如何让单行文字在高度固定的容器内垂直居中&#xff0c;但是您知道或者想过让行数不固定的文字在高度固定的容器内垂直居中呢&#xff1f;本文将会告诉你如何实现多行文字的垂直居中显示。 关于图片垂直居…

sticky-footer实现记录

sticky-footer是css中的一个经典问题&#xff1a; 当页面内容超出屏幕&#xff0c;页脚模块会像正常页面一样&#xff0c;被推到内容下方&#xff0c;需要拖动滚动条才能看到。 而当页面内容小于屏幕高度&#xff0c;页脚模块会固定在屏幕底部&#xff0c;就像是底边距为零的…

敏友的【敏捷个人】有感(3): 有感于“敏捷个人”讨论与练习

2010年我对个人管理进行了自己的一些思考&#xff0c;在2011年提出敏捷个人概念&#xff0c;并且在线上、线下进行了多次交流&#xff0c;在一些大会上也做过分享。现在&#xff0c;已经有很 多IT和非IT的敏友们知道并在践行敏捷个人&#xff0c;帮助自己更快的成长。我收到大家…

jQuery编写插件

引言&#xff1a; 在项目中不同页面经常要用到已经写好的交互&#xff0c;比如弹窗&#xff0c;比如下拉菜单&#xff0c;比如选项卡&#xff0c;比如删除... 此时如果每次都把代码copy一份无疑是一件比较麻烦并且无趣的事情&#xff0c;而且个人认为有些low了&#xff0c;我们…

webstorm中nodejs代码提示

preferences->languages&frameworks->Node.js and Npm中选择一个本地的node版本 preferences->languages&frameworks->JavaScript->Libraries 勾选node.js Core 回到代码

9012教你如何使用gulp4开发项目脚手架

本文将会介绍如何使用gulp4来搭建项目脚手架&#xff0c;如果您还在使用gulp3或更老的版本&#xff0c;您也以通过本文的一些思想将之前的项目进行完善&#xff0c;更新。如果gulp不是你们团队的重点&#xff0c;也可以移步我的另一篇文章:用 webpack 4.0 撸单页/多页脚手架 (j…

nodejs模块

nodejs模块遵循commonJS规范&#xff0c;但并不是完全实现规范&#xff0c;主要使用require引入模块、使用exports导出模块。 导出属性或方法 使用exports mouduleA.js&#xff1a; var say function(world){console.info("say: "world); }var sing function(wo…

Array.prototype.slice.call(arguments)

Array.prototype.slice.call(arguments)能将具有length属性的对象转成数组&#xff0c;除了IE下的节点集合&#xff08;因为ie下的dom对象是以com对象的形式实现的&#xff0c;js对象与com对象不能进行转换&#xff09;如&#xff1a;1 var a{length:2,0:first,1:second}; 2 Ar…

动态内存分配导致内存泄漏之处

摘要&#xff1a;举了几个动态内存分配过程中&#xff0c;发生内存泄漏的例子 1. 分配了内存&#xff0c;却没有及时删除,导致泄漏 1: void doSomething() 2: { 3: int *pnValue new int; 4: } 2. 为指针变量分配了一个内存&#xff0c;然后又让指针变量指向其他的值,导致…

nodejs常用模块-url

URL nodejs中针对url的常用方法。 node下打印url&#xff0c;结果&#xff1a; 引入url模块 var url require(url) 1、parse方法 将url解析成对象&#xff0c;parse方法原型&#xff1a; url.parse(urlStr[, parseQueryString][, slashesDenoteHost]) 可传递三个参数…

常用的javascript设计模式

请坚持 什么是设计模式 百度百科&#xff1a; 设计模式&#xff08;Design pattern&#xff09;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问&#xff0c;设计模式…

express项目创建

1、安装项目生成器 cnpm i express-generator -g express4.0以后&#xff0c;express与express-generator包分离&#xff0c;全局使用express命令生成项目骨架时应该安装express-generator包。 2、生成项目骨架 express server 默认使用的是jade模板&#xff0c;使用ejs模…

设置Jexus开机启动

把jexus做成系统服务&#xff0c;就像windows服务一样&#xff0c;可以设置开机启动就可以了。 第一、进入目录 /lib/systemd/system/ 第二、新建文件 vi jexus.service 文件内容 [Unit] Descriptionjexus Afternetwork.target[Service] Typeforking ExecStart/usr/jexus/jw…

iOS警告-This block declaration is not a prototype

关于警告 我们定义一个不带参数的block,通常是如下的方式 1typedefvoid (^UpdateSwichBtnBlock)();在xcode9中会提示一个警告 12This block declaration is not a prototypeInsert ‘void解决方式可以是如下的几种 1typedefvoid (^UpdateSwichBtnBlock)(void);但是这样,很多第三…