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. Nodejs koa2读取服务器图片返回给前端直接展示

    参考:https://blog.csdn.net/lihefei_coder/article/details/105435358 const fs = require('fs'); const pat ...

  2. 大数据Shell编程 之 常用正则表达式

    使用man grep查看grep的帮助文档,有如下内容: 可以看出,正则表达式由三类,分别是 基本的正则表达式(Basic Regular Expression 又叫 Basic RegEx 简称 B ...

  3. 了解Prometheus

    了解Prometheus 标签(空格分隔): 监控 介绍 Prometheus是用来收集和存储服务器的实时数据(比如:CPU.硬盘.服务响应.日志等),通过其丰富的运算函数,可以计算得到很多的服务性能 ...

  4. mac下编译安装grafana

    下载grafana源码 从grafana git 仓库下载指定的分支. 编译后端 我下载的时候,grafana的最新release是7.3.7,其需要安装go 1.15版本 生成可执行文件 进入项目根 ...

  5. 查询ES6兼容的网站

    http://kangax.github.io/compat-table/es6/ 查询es6兼容的网站

  6. 使用ECharts绘制网址径向树状图

    an.rustfisher.com有很多内容,很多页面.如果用一个树状图把所有页面展示出来会是什么效果? 第一时间想到了ECharts. 最后效果: https://an.rustfisher.com ...

  7. K8S最小硬件配置

  8. Jenkins(8)- CentOS 7.x 通过yum安装jenkins

    如果想从头学起Jenkins的话,可以看看这一系列的文章哦 https://www.cnblogs.com/poloyy/category/1645399.html 下载rpm包 sudo wget ...

  9. Element MenuNav刷新后点击菜单保留选中状态

    正常情况刷新后选中菜单会失去选中的状态,需要把default-active 当前激活菜单的 index保存下来这样刷新后读取 methods方法中增加 getSess() { this.active ...

  10. Identity用户管理入门五(登录、注销)

    一.建立LoginViewModel视图模型 using System.ComponentModel.DataAnnotations; namespace Shop.ViewModel { publi ...