张大胖上午遇到了一个棘手的问题,他在一个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的更多相关文章

  1. (转)一个故事讲完https

    (转)一个故事讲完https 2 1  序言 今天来聊一聊https 安全传输的原理. 在开始之前,我们来虚构两个人物, 一个是位于中国的张大胖(怎么又是你?!), 还有一个是位于米国的Bill (怎 ...

  2. 一个故事讲懂vue父子组件传值

    作者:李佳明同学链接:https://www.jianshu.com/p/2272b6ca0f0c 一个故事讲懂vue父子组件传值 讲故事前先讲代码 父组件向子组件传值 父组件数据传递给子组件可以通过 ...

  3. 线程的私有领地 ThreadLocal

    从名字上看,『ThreadLocal』可能会给你一种本地线程的概念印象,可能会让你联想到它是一个特殊的线程. 但实际上,『ThreadLocal』却营造了一种「线程本地变量」的概念,也就是说,同一个变 ...

  4. 一个故事讲清NIO

    假设某银行只有10个职员.该银行的业务流程分为以下4个步骤: 1) 顾客填申请表(5分钟): 2) 职员审核(1分钟): 3) 职员叫保安去金库取钱(3分钟): 4) 职员打印票据,并将钱和票据返回给 ...

  5. 【ZZ】终于有人把云计算、大数据和人工智能讲明白了!

    终于有人把云计算.大数据和人工智能讲明白了! https://mp.weixin.qq.com/s/MqBP0xziJO-lPm23Bjjh9w 很不错的文章把几个概念讲明白了...图片拷不过来... ...

  6. 一个故事搞懂Java并发编程

    最近在给别人讲解Java并发编程面试考点时,为了解释锁对象这个概念,想了一个形象的故事.后来慢慢发现这个故事似乎能讲解Java并发编程中好多核心概念,于是完善起来形成了了这篇文章.大家先忘记并发编程, ...

  7. 分享一个自制的 .net线程池

    扯淡 由于项目需求,需要开发一些程序去爬取一些网站的信息,算是小爬虫程序吧.爬网页这东西是要经过网络传输,如果程序运行起来串行执行请求爬取,会很慢,我想没人会这样做.为了提高爬取效率,必须使用多线程并 ...

  8. 一个故事讲清楚NIO

    转载请引用:一个故事讲清楚NIO 假设某银行只有10个职员.该银行的业务流程分为以下4个步骤: 1) 顾客填申请表(5分钟): 2) 职员审核(1分钟): 3) 职员叫保安去金库取钱(3分钟): 4) ...

  9. MEF入门之不求甚解,但力求简单能讲明白(四)

    上一篇我们已经可以获取各种FileHandler的实例和对应的元数据.本篇,我们做一个稍微完整的文件管理器. 1.修改接口IFileHandler,传入文件名 namespace IPart { pu ...

随机推荐

  1. 北斗卫星同步时钟(NTP网络时钟服务器)成功投运世界级工程港珠澳大桥

    北斗卫星同步时钟(NTP网络时钟服务器)成功投运世界级工程港珠澳大桥 北斗卫星同步时钟(NTP网络时钟服务器)成功投运世界级工程港珠澳大桥 本文由北京华人开创科技公司提供 原址在 http://www ...

  2. Linux笔记---硬盘添加

    BZ创建了一个块云硬盘添加到了虚拟机里,然后按照平时挂在U盘的方式去挂载硬盘: mount  -t ext4 /dev/vdb  /mnt/xxxx 结果报出以下错误: mount: wrong fs ...

  3. Updates were rejected because the remote contains work that you do(git报错解决方案)

    Updates were rejected because the remote contains work that you do(git报错解决方案) 今天向GitHub远程仓库提交本地项目文件时 ...

  4. Echars 6大公共组件详解

    Echars 六大组件详解 : title  tooltip toolbox legend  dataZoom visualMap 一.title标题详解 myTitleStyle = { color ...

  5. Gradient Boosted Regression

    3.2.4.3.6. sklearn.ensemble.GradientBoostingRegressor class sklearn.ensemble.GradientBoostingRegress ...

  6. [bzoj1999]树网的核

    从下午坑到网上..noip的数据太弱,若干的地方写挂结果还随便过= = 最坑的就是网上有些题解没考虑周全... 第一步是找直径,用两次bfs(或者dfs,Linux下系统栈挺大的..)解决.找出其中一 ...

  7. ARM的GPIO配置

  8. 学习Spring必学的Java基础知识(2)----动态代理

    Spring AOP使用动态代理技术在运行期织入增强的代码,为了揭示Spring AOP底层的工作机理,有必要对涉及到的Java知识进行学习.Spring AOP使用了两种代理机制:一种是基于JDK的 ...

  9. 利用nginx 虚拟主机、请求转发实现不同端口web访问

    一个服务器上挂一个网站实在是有点浪费:一个服务器上可以放多个网站:可以开启nginx的虚拟主机功能:利用访问的路径或者域名不同访问不同的文件夹:例如: 1.一台服务器上放多个网站使用nginx的配置文 ...

  10. Sql Server——数据增删改

    所谓数据的增删改就是在创建好数据库和表后向表中添加数据.删除表中的数据.更改表中的一些数据. 新增数据: 语法一: insert into 表名 values (数据内容)        --这里需要 ...