ThreadLocal底层
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底层的更多相关文章
- ThreadLocal底层原理学习
1. 是什么? 首先ThreadLocal类是一个线程数据绑定类, 有点类似于HashMap<Thread, 你的数据> (但实际上并非如此), 它所有线程共享, 但读取其中数据时又只能是 ...
- java 并发(七)--- ThreadLocal
文章部分图片来自参考资料 问题 : ThreadLocal 底层原理 ThreadLocal 需要注意什么问题,造成问题的原因是什么,防护措施是什么 ThreadLocal 概述 Threa ...
- Java -- 基于JDK1.8的ThreadLocal源码分析
1,最近在做一个需求的时候需要对外部暴露一个值得应用 ,一般来说直接写个单例,将这个成员变量的值暴露出去就ok了,但是当时突然灵机一动(现在回想是个多余的想法),想到handle源码里面有使用过Th ...
- 并发——深入分析ThreadLocal的实现原理
一.前言 这篇博客来分析一下ThreadLocal的实现原理以及常见问题,由于现在时间比较晚了,我就不废话了,直接进入正题. 二.正文 2.1 ThreadLocal是什么 在讲实现原理之前, ...
- 抛出这8个问题,检验一下你到底会不会ThreadLocal,来摸个底~
0.问题 和Synchronized的区别 存储在jvm的哪个区域 真的只是当前线程可见吗 会导致内存泄漏么 为什么用Entry数组而不是Entry对象 你学习的开源框架哪些用到了ThreadLoca ...
- 面试官:看你简历说写精通ThreadLocal,这几道题你都会吗?
问题 和Synchronized的区别 存储在jvm的哪个区域 真的只是当前线程可见吗 会导致内存泄漏么 为什么用Entry数组而不是Entry对象 你学习的开源框架哪些用到了ThreadLocal ...
- 我是如何用 ThreadLocal 虐面试官的?
我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. Thr ...
- 有关 ThreadLocal 的一切
早上好,各位新老读者们,我是七淅(xī). 今天和大家分享的是面试常驻嘉宾:ThreadLocal 当初鹅厂一面就有问到它,问题的答案在下面正文的第 2 点. 1. 底层结构 ThreadLocal ...
- 硬核剖析ThreadLocal源码,面试官看了直呼内行
工作面试中经常遇到ThreadLocal,但是很多同学并不了解ThreadLocal实现原理,到底为什么会发生内存泄漏也是一知半解?今天一灯带你深入剖析ThreadLocal源码,总结ThreadLo ...
随机推荐
- final、finally与finalize的区别?
一.final.finally与finalize的区别 final:final是一个修饰符,可以修饰类,方法和变量.final修饰类表示类不能被其它类继承,并且该类中的所有方法都会隐式的被final修 ...
- 遇到的C++ cli 转 C++ native 为C# 程序提供接口。
接口文件 /*++ (do not edit the above line) ************************************************************* ...
- Javascript - Vue - 路由
vue router.js 下载:vue-router.js, 该文件依赖于vue.js <script src="Scripts/vue-2.4.0.js">< ...
- 输入URL后浏览器的过程
In this article, I want my readers to get a picture of a very basic concept of the web world. Previo ...
- SpringBoot笔记(1)
一.Spring能做什么 微服务 响应式开发 分布式云开发 web开发 无服务开发(云) 批处理业务等 二.SpringBoot作用 能快速创建出生产级别的Spring应用 SpringBoot是整合 ...
- FPGA nios软核编写液晶屏LCD12864驱动程序源码以及注意事项,本人亲自踩坑,重要!!!
LCD12864引脚如下: FPGA开发板得提供,3.3v电压,5v电压,普通io都是3.3v电压 DB:数据脚,得用双向io,因为程序里面需要读取液晶的应答(普通io3.3v可以) E:?输出引脚即 ...
- Python__Xpath模块
import requests from lxml import etree # 导入xpath headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6 ...
- Python中 sys.argv[]
sys.argv[]是一个从程序外部获取参数的桥梁,从外部取得的参数可以是多个,所以获得的是一个列表(list),用[]提取其中的元素.其第一个元素是程序本身,随后才依次是外部给予的参数. 实例 im ...
- python使用UTF-8写入CSV中文乱码
使用encoding='utf-8',写入的文档是乱码. 解决办法: 修改encoding='utf-8-sig' 关于文件open()函数: open(path,'-模式-',encoding='u ...
- noip模拟40
\(\color{white}{\mathbb{名之以:海棠}}\) 考场 \(t1\) 看见题意非常简单,觉得可能是个简单题 暴力算出几个小样例右端点右移的时候左端点都是单调右移的,以为具有单调性, ...