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. 三 MongoDB进阶

    1 Limit方法 概念:查询指定数量的数据,该方法接受一个数字参数作为查询记录数的数量 举个例子:查询集合col中最多2条记录数 2 Skip方法 概念:查询到的结果集中,跳过指定数量的数据,该方法 ...

  2. IO流(File类--递归--过滤器--IO字节流--IO字符流--Properties集合--缓冲流--转换流--序列化流--打印流)

    一.File类 1.1概述 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作. 1.1.1相对路径与绝对路径 相对路径从盘符开始的路径,这是一个完整 ...

  3. 微信小程序学习笔记三 路由的基本使用

    小程序中路由的使用 1.1 页面路由 在小程序中, 所有页面的路由全部由框架进行管理 1.2 页面栈 框架以栈的形式维护了当前的所有页面, 当发生路由切换的时候, 页面栈的表现如下: 1.3 获取当前 ...

  4. HCNP Routing&Switching之OSPF虚连接

    前文我们了解了OSPF的网络类型.帧中继交换机映射以及路由器帧中继映射相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/15195762.html:今天我 ...

  5. go协程调度

    目录 前言 1. 线程池的缺陷 2.Goroutine 调度器 3.调度策略 3.1 队列轮转 3.2 系统调用 3.3 工作量窃取 4.GOMAXPROCS设置对性能的影响 参考 前言 Gorout ...

  6. shell脚本 批量添加删除用户

    2021-07-26 1.批量添加用户 # 编写脚本 vi add_student_50.sh # 添加用户组 student groupadd student # 添加用户 student1-stu ...

  7. for循环练习之打印三角形

    public class TestDemo01 { /** * 打印三角形 * 1.打印空格 * 2.打印三角形 */ public static void main(String[] args) { ...

  8. CSS实用技巧(中)

    前言 我们经常使用CSS,但是却不怎么了解CSS,本文主要对vertical-align.BFC.position中开发过程不怎么注意的特性进行简要总结,从本文中,你将了解到以下内容: vertica ...

  9. 使用HttpRunner3+Allure+Jenkins实现Web接口自动化测试

    陆续给不同项目做了Web接口自动化测试,在尝试不同方法的同时会有新的体会.最近用到了HttpRunner3,本文将记录使用HttpRunner3+Allure+Jenkins在项目中快速实现Web接口 ...

  10. Http协议 压缩

    先说结论:Request和Response可以设置gzip压缩从而节省流量/带宽,这是一个不常见的问题.具体可见连接:HTTP 协议之压缩 最近测试代理服务器时,意外的发现使用urlopen请求百度首 ...