多线程如果不理解透彻, 那么 ThreadLocal 始终是有些会有所迷糊的。
ThreadLocal 本身的命名有有问题, 这些美国精英整出来的技术,再加上一个奇怪的命名。对我们中国人来说,就是一场场的灾难。
如下的问题, 你觉得输出是多少呢?
/**
* Created by lk on 2017/6/8.
*/
public class ThreadLocalTest extends Thread { ThreadLocal<Long> threadLocal = new ThreadLocal<Long>() {
@Override
protected Long initialValue() {
return 2L;
}
}; public ThreadLocalTest() {
setThreadLocal(new ThreadLocal<Long>());
} @Override
public void run() {
show();
} public void setThreadLocal(ThreadLocal<Long> threadLocal) {
this.threadLocal = threadLocal;
threadLocal.set(10L);
} public void show() {
System.out.println( threadLocal.get());
} public static void main(String[] args) {
ThreadLocalTest ThreadLocalTest = new ThreadLocalTest(); ThreadLocalTest.run();
ThreadLocalTest.start();
}
}

答案是

10

null

这里有几个陷阱。

首先,ThreadLocalTest.run(); 这行由于要启动一个新的线程 以及它要初始化一个map(ThreadLocal 内部的东西)等, 它执行show 方法的时间通常会ThreadLocalTest.start();  所以其实是 ThreadLocalTest.run();  打印了null, 而 ThreadLocalTest.start(); 输出了 10。 就是说 后者比前者先输出。我们 把show 方法改成下面的样子:

System.out.println(" local = " + Thread.currentThread().getName() + "     " + threadLocal.get());
就会得到:

local = main 10
local = Thread-0 null

其次,ThreadLocalTest.run();  和 ThreadLocalTest.start();  方法看起来是执行了一样的代码, 其实不然, 这就是ThreadLocal 的非常的令人迷惑的地方。  要理解ThreadLocal , 关键在于理解其 get / set 方法, ThreadLocal  和 Thread 的关系, ThreadLocal  和 ThreadLocalMap 的关系 。一定要理清。  我们可以知道。 这里有两个线程, 一个是 main, 一个是 ThreadLocalTest。 

main 线程的执行路径是: new 一个  ThreadLocalTest ,new 的过程中, 修改了当前类变量 threadLocal(其初始值是2L),让其指向了一个新的 ThreadLocal , 其没有初始值,也就是 null (查看源码可知)。 然后又将 其值设置为 10L。  这里一定要搞清楚, 最开始的那个 threadLocal 变量的堆实例已经无法获取了, 已经变成垃圾了, 随时可能被GC了, 现在的threadLocal 变量仅仅是一个 引用, 其指向的实例 已经不是原来那个了, 初始值已经变成了 null了。threadLocal.set(10L); 改成  this.threadLocal.set(10L); 是没有任何影响的, 他们是同一个实例。

至此, 我们应该明白了, main线程对应的 ThreadLocalMap 实例存放的 threadLocal 对应的 key 的 值 是 10L, 故 main 的start 方法调用的 show , get 到的 threadLocal 的值 就是 10。

ThreadLocalTest 线程的执行路径 是明显不同的, ThreadLocalTest 只管启动一个线程, 然后执行 run 方法,run 方法调用的 show , get 到的 threadLocal 的值 是多少呢? 我们先看一下 get 的源码比较好:

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

ThreadLocalTest 线程执行到 ThreadLocalMap map = getMap(t);  获取的 map 一定是 null, 因为ThreadLocalTest 线程 是刚刚创建的,其 对应的 ThreadLocalMap 一定是 null ( 查看 Thread 的源码可知),  这样, ThreadLocalMap 立即执行 return setInitialValue(); 尝试设置 初始值,然后返回初始值。 setInitialValue 方法 其实主要是获取了  initialValue 方法的值, 然后设置到了 ThreadLocalMap 中去。  前面的分析我们得知,现在的 threadLocal 已经被悄悄改变了, 它没有复写initialValue 方法, 其初始值就是默认值, 就是null。 故 get 的结果 就是 null 。 至此, 分析完毕。

欢迎热爱底层技术的人和我一起遨游技术的海洋。还有不懂的请留言。

  

详解一个ThreadLocal 的谜题的更多相关文章

  1. ThreadLocal详解,ThreadLocal源码分析,ThreadLocal图解

    本文脉路: 概念阐释 ---->  原理图解  ------> 源码分析 ------>  思路整理  ----> 其他补充. 一.概念阐述. ThreadLocal 是一个为 ...

  2. java多线程详解(5)-Threadlocal用法

    ThreadLocal是什么 早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路. 使用这个工具类可以很简洁 ...

  3. 详解一个自己原创的正则匹配IP的表达式

    这里给大家详细讲解一下一个匹配IP地址的正则表达式, 有关正则方面的知识,会在详细的讲解中提到. 在讲解之前,我先给大家介绍一下,ip地址的生成规则. IP地址,是由32位数字二进制转为四个十进制的字 ...

  4. pc端的企业网站(IT修真院test9)详解一个响应式完成的pc端项目

    一:引入bootstrap框架 昨天一直被bootstrap栅格系统折磨. why? 我本来想一边码字,一边学习栅格布局的.but不成功.这时我头脑已经昏了. 下午,我查看了bootstrap的官网, ...

  5. Android开发——Android的消息机制详解

    )子线程默认是没有Looper的,Handler创建前,必须手动创建,否则会报错.通过Looper.prepare()即可为当前线程创建一个Looper,并通过Looper.loop()来开启消息循环 ...

  6. Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)

    Android XML shape 标签使用详解   一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...

  7. iOS开发技巧系列---详解KVC(我告诉你KVC的一切)

    KVC(Key-value coding)键值编码,单看这个名字可能不太好理解.其实翻译一下就很简单了,就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值.而不需 ...

  8. 13 SQLiteOpenHelper SQLiteDatabase详解

    创建数据库: 1. 创建一个类继承SQLiteOpenHelper 2. 创建继承对象 new SQLiteOpenHelper() 3. 用创建的对象获取可写或者可读的SQLiteDatabase ...

  9. Touch事件在移动端web开发中的详解

    一.pc端事件回顾 HTML事件.DOM0事件.DOM2事件 事件对象. 如果上述概念不清楚,请先去了解. 二.移动端事件简介 2.1 pc端事件在移动端的问题 ​ 移动设备主要特点是不配备鼠标,键盘 ...

随机推荐

  1. python基本知识点

    1.基本数据类型 1.1int 字符串转换为数字,比如 a = “123” print(type(a) , a) b = int(a) print(type(b),b) num = “b” v = i ...

  2. JTextArea利用JScrollpane增加文本域滚轮(滚动条)

    JTextArea加滚轮的几种方法 JTextArea txaDisplay = new JTextArea(); JScrollPane scroll = new JScrollPane(txaDi ...

  3. 黄聪:ffmpeg基本用法(转)

    FFmpeg FFmpeg 基本用法 本课要解决的问题 1.FFmpeg的转码流程是什么? 2.常见的视频格式包含哪些内容吗? 3.如何把这些内容从视频文件中抽取出来? 4.如何从一种格式转换为另一种 ...

  4. appium 元素文件 -查找元素 封装思路和方法

    方法1. try: target="//android.widget.TextView[@text='立即體驗']" element = WebDriverWait(dr,5,0. ...

  5. 量化交易(Quantitative Trading)

    什么是量化交易 量化交易是指借助现代统计学和数学的方法,利用计算机技术来进行交易的证券投资方式.量化交易从庞大的历史数据中海选能带来超额收益的多种“大概率”事件以制定策略,用数量模型验证及固化这些规律 ...

  6. 如何进行CodeReview

    一.代码规范的要点 代码规范主要分为风格规范与设计规范两大类: 1.代码风格规范 主要是文字上的规定,看似表面文章,实际上非常重要. 具体有如下几个方面: (1)缩进 (2)行宽 (3)断行/空白行 ...

  7. Shell案例:图案打印

    打印三角形图案 #!/bin/bash echo "Stars" ; i<=; i++ )) do ; j<=i; j++ )) do echo -n " * ...

  8. NFS应用场景及环境搭建

    两台虚拟机,一台做服务端(server)用来存储,一台做客户端(client)用来访问. 注意,两台虚拟机都已经挂载完光盘,并配置好yum源.客户端client已经安装好lamp环境,服务端不做任何处 ...

  9. [UE4]控制流

    虽然官方文档说复杂的蓝图循环是会跨域多帧运行,但实际上测试下来,如果在循环体进行大量复杂的运算,不足以在一帧内完成时,游戏就会在当前帧卡住,直到循环结束为止. 一.Switch Switch可以在所有 ...

  10. [UE4]Set Skeletal Mesh,在蓝图中设置骨骼模型