1. 首先我们来看一下他的使用

public class ThreadLocalTest {

    public static void main(String[] args) {

        MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread(); thread1.start();
thread2.start();
}
}
class MyThread extends Thread{
//泛型类型也就是key值的类型
private static ThreadLocal<String> threadLocal = new ThreadLocal<>(); @Override
public void run() {
//生成一个四位的随机字符串
String id = UUID.randomUUID().toString().substring(0,4);
System.out.println(Thread.currentThread().getName()+"对应的数字为"+id);
//保存到threadLocal中
threadLocal.set(id); for (int i = 0; i < 20; i++) {
//threadLocal.get()从中取出
System.out.println(Thread.currentThread().getName()+"的数据"+threadLocal.get());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

我们观察打印输出:可以发现,每一条线程对应的数据是固定的,这个数据是每个线程独有的,这就是ThreadLocal

Thread-0对应的数字为ac58
Thread-1对应的数字为fec2
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-0的数据ac58
Thread-1的数据fec2
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-0的数据ac58
Thread-1的数据fec2
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-0的数据ac58
Thread-1的数据fec2
Thread-1的数据fec2
Thread-0的数据ac58
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-1的数据fec2
Thread-0的数据ac58
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2

2. 接下来我们来分析它的源码实现

1. 首先观察构造器,发现什么也没有

2. 然后我们再来看刚才调用的方法threadLocal.set(id);

public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//传入当前线程,返回一个ThreadLocalMap,那么这个map是什么东西,我们再进去看一下
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

2. 1--getMap(t)

ThreadLocalMap getMap(Thread t) {
//直接返回了线程对象里面的一个变量,我们再来看看这个变量是什么东西
return t.threadLocals;
}
2. 1--1--return t.threadLocals;

这次我们来到了Thread类中

      //我们可以看到这个是一个ThreadLocal里面的一个静态内部类
ThreadLocal.ThreadLocalMap threadLocals = null;
2. 1--1--ThreadLocalMap

我们稍微观察一下就可以看到,这个的实现大致类似于HashMap

其中的"对"的key值时ThreadLocal对象,值时Object类型

Entry(ThreadLocal<?> k, Object v)

这时我们也可以看出来,ThreadLocal中key值并不是很多地方说的当前线程对象,而是ThreadLocal对象

static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
......
}

2. 1--我们接下来回到set方法中

      ThreadLocalMap map = getMap(t);//拿到map之后
if (map != null)//如果为空,则map调用set
//因为当前是ThreadLocal调用这个set方法,所以key值的this是当前ThreadLocal对象
map.set(this, value);
else//否则创建map,这个创建没什么好说的,就是new ThreadLocalMap
createMap(t, value);

3. 在接下来就轮到我们的get方法了threadLocal.get()

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//上面的两步也一样,获取ThreadLocalMap对象
if (map != null) {
//调用map的getEntry,看3.1
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
//将拿到的entry的value返回回去,也就是我们保存的值
return result;
}
}
return setInitialValue();
}

3. 1--获取当前的entry

        private Entry getEntry(ThreadLocal<?> key) {
//计算当前的ThreadLocal对应的位置,详情可以去了解HashMap
int i = key.threadLocalHashCode & (table.length - 1);
//拿到对应的entry
Entry e = table[i];
if (e != null && e.get() == key)
//返回entry
return e;
else
return getEntryAfterMiss(key, i, e);
}

3. 最后我们也可以自己实现一个简单的ThreadLocal

public class ThreadLocalUtils {

    //key值是当前线程的id
private Map<Long, Object> map = new HashMap<>(); public Object get() {
long id = Thread.currentThread().getId();
return map.get(id);
} public void set(Object value) {
long id = Thread.currentThread().getId();
//将旧的引用拿到,换成新值
Object o = map.get(id);
o = value;
}
}

使用

public class MyThreadLocalTest {

    public static void main(String[] args) {

        MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread(); thread1.start();
thread2.start();
} } class MyMyThread extends Thread{ private static ThreadLocalUtils threadLocal= new ThreadLocalUtils(); @Override
public void run() { String id = UUID.randomUUID().toString().substring(0,4);
System.out.println(Thread.currentThread().getName()+"对应的数字为"+id);
threadLocal.set(id); for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"的数据"+threadLocal.get());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

ThreadLocal底层的更多相关文章

  1. ThreadLocal底层原理学习

    1. 是什么? 首先ThreadLocal类是一个线程数据绑定类, 有点类似于HashMap<Thread, 你的数据> (但实际上并非如此), 它所有线程共享, 但读取其中数据时又只能是 ...

  2. java 并发(七)--- ThreadLocal

         文章部分图片来自参考资料 问题 : ThreadLocal 底层原理 ThreadLocal 需要注意什么问题,造成问题的原因是什么,防护措施是什么 ThreadLocal 概述 Threa ...

  3. Java -- 基于JDK1.8的ThreadLocal源码分析

    1,最近在做一个需求的时候需要对外部暴露一个值得应用  ,一般来说直接写个单例,将这个成员变量的值暴露出去就ok了,但是当时突然灵机一动(现在回想是个多余的想法),想到handle源码里面有使用过Th ...

  4. 并发——深入分析ThreadLocal的实现原理

    一.前言   这篇博客来分析一下ThreadLocal的实现原理以及常见问题,由于现在时间比较晚了,我就不废话了,直接进入正题. 二.正文 2.1 ThreadLocal是什么   在讲实现原理之前, ...

  5. 抛出这8个问题,检验一下你到底会不会ThreadLocal,来摸个底~

    0.问题 和Synchronized的区别 存储在jvm的哪个区域 真的只是当前线程可见吗 会导致内存泄漏么 为什么用Entry数组而不是Entry对象 你学习的开源框架哪些用到了ThreadLoca ...

  6. 面试官:看你简历说写精通ThreadLocal,这几道题你都会吗?

    问题 和Synchronized的区别 存储在jvm的哪个区域 真的只是当前线程可见吗 会导致内存泄漏么 为什么用Entry数组而不是Entry对象 你学习的开源框架哪些用到了ThreadLocal ...

  7. 我是如何用 ThreadLocal 虐面试官的?

    我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. Thr ...

  8. 有关 ThreadLocal 的一切

    早上好,各位新老读者们,我是七淅(xī). 今天和大家分享的是面试常驻嘉宾:ThreadLocal 当初鹅厂一面就有问到它,问题的答案在下面正文的第 2 点. 1. 底层结构 ThreadLocal ...

  9. 硬核剖析ThreadLocal源码,面试官看了直呼内行

    工作面试中经常遇到ThreadLocal,但是很多同学并不了解ThreadLocal实现原理,到底为什么会发生内存泄漏也是一知半解?今天一灯带你深入剖析ThreadLocal源码,总结ThreadLo ...

随机推荐

  1. ASP.NET Core:ASP.NET Core程序使用Docker部署

    一.前言 这篇文章介绍如何将一个ASP.NET Core应用程序在Docker中进行部署.开发工具使用的是Visual Studio 2019和VS Code. 二.使用Docker部署 我们选择新建 ...

  2. java 内存泄露的几种情况

    内存泄漏定义(memory leak):一个不再被程序使用的对象或变量还在内存中占有存储空间. 一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出.内存溢出 out of memory ...

  3. 深入浅出Mybatis系列(九)---缓存

    MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存. 1.默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启. 2.二级缓存需要手动开启和配置,他是基于namesp ...

  4. Bing每日壁纸的RESTful接口实现

    0x00 存在意义 权且当作Docker打包的练习. 显然可以通过构造请求获得每天的壁纸,但是如果想要优雅地在其它地方使用这一网络资源,封装一个RESTful API将会保证整洁美观,在编写CSS等场 ...

  5. [源码解析] 深度学习流水线并行 PipeDream(3)--- 转换模型

    [源码解析] 深度学习流水线并行 PipeDream(3)--- 转换模型 目录 [源码解析] 深度学习流水线并行 PipeDream(3)--- 转换模型 0x00 摘要 0x01 前言 1.1 改 ...

  6. K8S资源编排(yaml)

    1.yaml的格式 2.yaml的组成部分 3.yaml常用字段的含义 4.yaml编写方式 (1)方式一:使用kubectl create命令生成yaml文件,然后修改 (2)方式2:在已经部署好的 ...

  7. MySQL——备份与恢复

    MySQL数据库备份和恢复 --备份类型: 1.热备份(只有InnoDB支持)(不影响业务,最好的方式) 2.冷备份( 阻止用户访问) 3.温备份 --备份方式: 1.逻辑备份(文本表示:SQL语句) ...

  8. NOIP模拟测试17&18

    NOIP模拟测试17&18 17-T1 给定一个序列,选取其中一个闭区间,使得其中每个元素可以在重新排列后成为一个等比数列的子序列,问区间最长是? 特判比值为1的情况,预处理比值2~1000的 ...

  9. 2021 年 9 月 TIOBE 指数 C# 增长突破 1.2%

    TIOBE 编程社区指数是编程语言流行程度的指标.该指数每月更新一次.评级基于全球熟练工程师.课程和第三方供应商的数量.谷歌.必应.雅虎.维基百科.亚马逊.YouTube 和百度等流行搜索引擎用于计算 ...

  10. redis 主从复制详解

    引言 我们之前操作 Redis 都是单机版,但是实际应用中没人使用单机版,都是搭建集群的方式.这篇文章要介绍的主从复制,是指将一台 Redis 服务器的数据,复制到其他 Redis 服务器,我们将前者 ...