张大胖上午遇到了一个棘手的问题,他在一个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. ASP.NET Core Web API下事件驱动型架构的实现(二):事件处理器中对象生命周期的管理

    在上文中,我介绍了事件驱动型架构的一种简单的实现,并演示了一个完整的事件派发.订阅和处理的流程.这种实现太简单了,百十行代码就展示了一个基本工作原理.然而,要将这样的解决方案运用到实际生产环境,还有很 ...

  2. .NET Core单文件发布静态编译AOT CoreRT

    .NET Core单文件发布静态编译AOT CoreRT,将.NET Core应用打包成一个可执行文件并包含运行时. 支持Windows, MacOS and Linux x64 w/ RyuJIT ...

  3. 《JAVA程序设计与实例》记录与归纳--继承与多态

    继承与多态 概念贴士: 1. 继承,即是在已经存在的类的基础上再进行扩展,从而产生新的类.已经存在的类成为父类.超类和基类,而新产生的类成为子类或派生类. 2. Java继承是使用已存在的类的定义作为 ...

  4. IdentityServer(13)- 添加JavaScript客户端

    这个快速入门将展示如何构建一个JavaScript客户端应用程序. 用户将登录到IdentityServer,使用IdentityServer发出的访问令牌调用Web API,并注销IdentityS ...

  5. 【HTML_标签大全】

    HTML标签大全 标签 描述 标签类型 备注 <!--...--> 定义注释 / 单标签 <!DOCTYPE> 定义文档类型 / 单标签 <head></he ...

  6. 利用cmd制作一句话图片马

    先cd进在图片和一句话木马里面的文件夹 假设选择我的图片是:1.jpg 一句话是:2.php 命令:copy 1.jpg/a+2.php/b 生成的图片马

  7. HDU1754-I Hate It-线段树

    I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  8. ucosii --任务就绪表

    任务就绪表的任务就是高效的找出当前优先级最高的就绪任务. 由任务就绪表OSRdyTbl和任务就绪组OSRdyGrb组成,OSRdyTbl每一个位都记录着一个任务的就绪状态, 0非就绪1就绪,OSRdy ...

  9. 互联网App应用程序测试流程及测试总结

    互联网App应用程序测试流程及测试总结 1. APP测试基本流程 1.1流程图 仍然为测试环境 Pass 1.2测试周期 测试周期可按项目的开发周期来确定测试时间,一般测试时间为两三周(即15个工作日 ...

  10. 解决php的sha1和java的sha1(DigestUtils.sha1Hex)产生的字符串不相等的问题

    最近对接某个第三方服务,其中对接某些api需要用到他们的签名回调,根据他们传来的get参数和apiSecret进行拼接并使用sha1加密,然后返回弄成jsonp的格式返回,出于菜鸟的本能,首先是下载了 ...