一个故事讲明白线程的私家领地:ThreadLocal
张大胖上午遇到了一个棘手的问题,他在一个AccountService中写了一段类似这样的代码:
Context ctx = new Context(); ctx.setTrackerID(.....)
然后这个AccountService 调用了其他Java类,不知道经过了多少层调用以后,最终来到了一个叫做AccountUtil的地方,在这个类中需要使用Context中的trackerID来做点儿事情:
很明显,这个AccountUtil没有办法拿到Context对象, 怎么办?
张大胖想到,要不把Context对象一层层地传递下去,这样AccountUtil不就可以得到了吗?

可是这么做改动量太大!涉及到的每一层函数调用都得改动,有很多类都不属于自己的小组管理,还得和别人协调。
更要命的是有些类根本就没有源码,想改都改不了。
这也难不住我,张大胖想:可以把那个set/get TrackerID的方法改成静态(static)的,这样不管跨多少层调用都没有问题!
public class Context{
public static String getTrackerID(){
......
}
public static void setTrackerID(String id){
......
}
}

这样就不用一层层地传递了,Perfect!
张大胖得意洋洋地把代码提交给Bill做Review。
Bill看了一眼就指出了致命的问题: 多线程并发的时候出错!
张大胖恨不得找个地缝钻进去:又栽在多线程上面了,这次犯的还是低级错误!
线程1调用了Context.setTrackerID(), 线程2 也调用了Context.setTrackerID(),数据互相覆盖,不出乱子才怪。
张大胖感慨地说:“像我这样中情况,需要在某处设置一个值,然后经过重重方法调用,到了另外一处把这个值取出来,又要线程安全,实在是不好办啊, 对了,我能不能把这个值就放到线程中? 让线程携带着这个值到处跑,这样我无论在任何地方都可以轻松获得了!”
Bill说:“有啊,每个线程都有一个私家领地! 在Thread这个类中有个专门的数据结构,你可以放入你的TrackerID,然后到任何地方都可以把这个TrackerID给取出来。”
“这么好? ”
张大胖打开JDK中的Thread类,仔细查看,果然在其中有个叫做threadLocals的变量,还是个Map类型 , 但是在Thread类中却没有对这个变量操作的方法。
看到张大胖的疑惑,Bill说:“也许你注意到了,这个变量不是通过Thread的访问的,对他的访问委托给了ThreadLocal这个类。”
“那我怎么使用它?”
“非常简单, 你可以轻松创建一个ThreadLocal类的实例:
ThreadLocal<String> threadLocalA= new ThreadLocal<String>();
线程1: threadLocalA.set("1234");
线程2: threadLocalA.set("5678");
像‘1234’, ‘5678’这些值都会放到自己所属的线程对象中。”

“等你使用的时候,可以这么办:”
线程1: threadLocalA.get() --> "1234" 线程2: threadLocalA.get() --> "5678"
“明白了,相当于把各自的数据放入到了各自Thread这个对象中去了,每个线程的值自然就区分开了。 可是我不明白的是为什么那个数据结构是个map 呢?”
“你想想,假设你创建了另外一个threadLocalB:”
ThreadLocal<Integer> threadLocalB = new ThreadLocal<Integer>(); 线程1: threadLocalB.set(30); 线程2: threadLocalB.set(40);
那线程对象的Map就起到作用了:
“明白了,这个私家领地还真是好用,我现在就把我那个Context给改了,让它使用ThreadLocal:”
public class Context {
private static final ThreadLocal<String> mThreadLocal
= new ThreadLocal<String>();
public static void setTrackerID(String id) {
mThreadLocal.set(id);
}
public static String getTrackerID() {
return mThreadLocal.get();
}
}
小结:
ThreadLocal这个名字起得有点让人误解, 很容易让人认为是“本地线程”, 其实是用来维护本线程的变量。 对照着上面的原理讲解,我想大家可以自行去看ThreadLocal的源码,轻松理解。
ThreadLocal 并不仅仅是Java中的概念,其他语言例如Python,C#中也有,作用类似。
ThreadLocal在日常工作中用得不多,但是在框架(如Spring)中是个基础性的技术,在事务管理,AOP等领域都能找到。
(完)
最近总是有人和团长要练手的大型实战项目案例,团长在这里可以推荐一些:
Java开发天猫购物车核心支付系统【电商平台核心技术】
Java开发人工智能扫一扫人脸识别系统【人工智能最新实战技术】
Java开发百度云大数据管理系统【百度云最新技术】
Java开发网易163邮件群发系统 【邮箱核心技术】
Java开发人工智能IM客服系统【京东IM核心技术】
......
有需要这些教程的朋友直接来团长的QQ群吧。
群号码:589809992
一个故事讲明白线程的私家领地:ThreadLocal的更多相关文章
- (转)一个故事讲完https
(转)一个故事讲完https 2 1 序言 今天来聊一聊https 安全传输的原理. 在开始之前,我们来虚构两个人物, 一个是位于中国的张大胖(怎么又是你?!), 还有一个是位于米国的Bill (怎 ...
- 一个故事讲懂vue父子组件传值
作者:李佳明同学链接:https://www.jianshu.com/p/2272b6ca0f0c 一个故事讲懂vue父子组件传值 讲故事前先讲代码 父组件向子组件传值 父组件数据传递给子组件可以通过 ...
- 线程的私有领地 ThreadLocal
从名字上看,『ThreadLocal』可能会给你一种本地线程的概念印象,可能会让你联想到它是一个特殊的线程. 但实际上,『ThreadLocal』却营造了一种「线程本地变量」的概念,也就是说,同一个变 ...
- 一个故事讲清NIO
假设某银行只有10个职员.该银行的业务流程分为以下4个步骤: 1) 顾客填申请表(5分钟): 2) 职员审核(1分钟): 3) 职员叫保安去金库取钱(3分钟): 4) 职员打印票据,并将钱和票据返回给 ...
- 【ZZ】终于有人把云计算、大数据和人工智能讲明白了!
终于有人把云计算.大数据和人工智能讲明白了! https://mp.weixin.qq.com/s/MqBP0xziJO-lPm23Bjjh9w 很不错的文章把几个概念讲明白了...图片拷不过来... ...
- 一个故事搞懂Java并发编程
最近在给别人讲解Java并发编程面试考点时,为了解释锁对象这个概念,想了一个形象的故事.后来慢慢发现这个故事似乎能讲解Java并发编程中好多核心概念,于是完善起来形成了了这篇文章.大家先忘记并发编程, ...
- 分享一个自制的 .net线程池
扯淡 由于项目需求,需要开发一些程序去爬取一些网站的信息,算是小爬虫程序吧.爬网页这东西是要经过网络传输,如果程序运行起来串行执行请求爬取,会很慢,我想没人会这样做.为了提高爬取效率,必须使用多线程并 ...
- 一个故事讲清楚NIO
转载请引用:一个故事讲清楚NIO 假设某银行只有10个职员.该银行的业务流程分为以下4个步骤: 1) 顾客填申请表(5分钟): 2) 职员审核(1分钟): 3) 职员叫保安去金库取钱(3分钟): 4) ...
- MEF入门之不求甚解,但力求简单能讲明白(四)
上一篇我们已经可以获取各种FileHandler的实例和对应的元数据.本篇,我们做一个稍微完整的文件管理器. 1.修改接口IFileHandler,传入文件名 namespace IPart { pu ...
随机推荐
- GPS校时器,GPS时钟装置,NTP网络时间服务器
GPS校时器,GPS时钟装置,NTP网络时间服务器 GPS校时器,GPS时钟装置,NTP网络时间服务器 GPS校时器,GPS时钟装置,NTP网络时间服务器 GPS校时器,GPS时钟装置,NTP网络时间 ...
- Android CoordinatorLayout、AppBarLayout、DrawerLayout、NavigationView 的使用及问题小结
这里只对Material Design中这几种组件使用的重要部分以及容易出现问题的地方进行汇总(遇坑请直接看最后常见问题部分),详细用法请自行查阅官方文档 一.CoordinatorLayout 介绍 ...
- XSS Challenges闯关笔记
前言 做xss做疯了再来一个. 地址:https://xss-quiz.int21h.jp/ ,这个貌似是日本的一个安全研究员yamagata21做的. 做到第九关就跪了,而总共有二十关.一半都还没有 ...
- ASP.NET MVC 设置区域默认定向
public override void RegisterArea(AreaRegistrationContext context) { context.MapRoute( "m_defau ...
- 删除链表中间节点和a/b处的节点
[题目]: 给定链表的头节点 head,实现删除链表的中间节点的函数. 例如: 步删除任何节点: 1->2,删除节点1: 1->2->3,删除节点2: 1->2->3-& ...
- python之dictionary
1.python3字典 字典是另一种可变容器模型,且可存储任意类型对象. 字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中 d ...
- 【转载】vim复制时的缩进
:set paste vi/vim鼠标中键粘贴文本默认情况是自动缩进的,如果粘贴的是多行的文本,则第二行缩进1个tab,第三行缩进2个tab……依此类推,到最后文本会面目全非,解决办法是粘贴之前设置为 ...
- SpringBoot入坑-持久化操作
前面内容中我们已经了解到了SpringBoot关于参数传递的相关知识,本篇我们一起来学习一下SpringBoot关于数据库持久化操作的知识,这里我们使用JPA进行数据库的持久化操作. 首先由于我们需要 ...
- python 使用paramiko模块上传本地文件到ssh
我们要了解几个函数: paramiko.Tranport(("目标ip,端口"))#这是上传目标的IP和端口 paramiko.SFTPClient.from_tranport() ...
- 【请您听我说】PHP语法特点的一些看法
一.基本认识 PHP是干什么的?百度百科上提到说:PHP就是一门脚本语言,开发用的,相信这个你们只要去搜一下,就会有一大堆关于PHP概念的解释. 相信我们对PHP的初步认识是从浏览器开始的吧,当我们每 ...