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. final、finally与finalize的区别?

    一.final.finally与finalize的区别 final:final是一个修饰符,可以修饰类,方法和变量.final修饰类表示类不能被其它类继承,并且该类中的所有方法都会隐式的被final修 ...

  2. 遇到的C++ cli 转 C++ native 为C# 程序提供接口。

    接口文件 /*++ (do not edit the above line) ************************************************************* ...

  3. Javascript - Vue - 路由

    vue router.js 下载:vue-router.js, 该文件依赖于vue.js <script src="Scripts/vue-2.4.0.js">< ...

  4. 输入URL后浏览器的过程

    In this article, I want my readers to get a picture of a very basic concept of the web world. Previo ...

  5. SpringBoot笔记(1)

    一.Spring能做什么 微服务 响应式开发 分布式云开发 web开发 无服务开发(云) 批处理业务等 二.SpringBoot作用 能快速创建出生产级别的Spring应用 SpringBoot是整合 ...

  6. FPGA nios软核编写液晶屏LCD12864驱动程序源码以及注意事项,本人亲自踩坑,重要!!!

    LCD12864引脚如下: FPGA开发板得提供,3.3v电压,5v电压,普通io都是3.3v电压 DB:数据脚,得用双向io,因为程序里面需要读取液晶的应答(普通io3.3v可以) E:?输出引脚即 ...

  7. Python__Xpath模块

    import requests from lxml import etree # 导入xpath headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6 ...

  8. Python中 sys.argv[]

    sys.argv[]是一个从程序外部获取参数的桥梁,从外部取得的参数可以是多个,所以获得的是一个列表(list),用[]提取其中的元素.其第一个元素是程序本身,随后才依次是外部给予的参数. 实例 im ...

  9. python使用UTF-8写入CSV中文乱码

    使用encoding='utf-8',写入的文档是乱码. 解决办法: 修改encoding='utf-8-sig' 关于文件open()函数: open(path,'-模式-',encoding='u ...

  10. noip模拟40

    \(\color{white}{\mathbb{名之以:海棠}}\) 考场 \(t1\) 看见题意非常简单,觉得可能是个简单题 暴力算出几个小样例右端点右移的时候左端点都是单调右移的,以为具有单调性, ...