Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >ThreadLocal源码阅读

ThreadLocal源码阅读

作者头像
海涛
发布于 2019-12-16 08:58:57
发布于 2019-12-16 08:58:57
46600
代码可运行
举报
文章被收录于专栏:海涛技术日常海涛技术日常
运行总次数:0
代码可运行

首先,从set方法入手,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    // ThreadLocal
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);//这里可以看出,从Thread对象获取了一个ThreadLocalMap
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
//Thread

    ThreadLocal.ThreadLocalMap threadLocals = null;

接下来,看如何创建的 这个特殊的Map

首次创建

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];//初始化空间为16
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);//机损索引
    table[i] = new Entry(firstKey, firstValue);//赋值
    size = 1;
    setThreshold(INITIAL_CAPACITY);//计算扩容的临界点
}

注意点:Entry 的 key 采用弱引用,在内存不足时候,会被回收

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);// k 采用弱引用,在内存不足时候,会被回收
        value = v;
    }
}

到这里,细节不说 应该了解大体的数据结构

每个线程独有一个 Map,Map里的存储结构为 Entry <ThreadLocal,Object> 数组

如果已存在,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void set(ThreadLocal<?> key, Object value) {

    // We don't use a fast path as with get() because it is at
    // least as common to use set() to create new entries as
    // it is to replace existing ones, in which case, a fast
    // path would fail more often than not.

    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);

    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {//循环,直到i位置无数据
        ThreadLocal<?> k = e.get();

        if (k == key) {//同一个ThreadLocal 替换 value
            e.value = value;
            return;
        }

        if (k == null) {// k 为空,替换 
            replaceStaleEntry(key, value, i);
            return;
        }
    }

    tab[i] = new Entry(key, value);
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)//清除无效Entry 并扩容
        rehash();
}

扩容

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void resize() {
    Entry[] oldTab = table;
    int oldLen = oldTab.length;
    int newLen = oldLen * 2;// 双倍空间
    Entry[] newTab = new Entry[newLen];
    int count = 0;

    for (int j = 0; j < oldLen; ++j) {
        Entry e = oldTab[j];
        if (e != null) {
            ThreadLocal<?> k = e.get();
            if (k == null) {
                e.value = null; // Help the GC
            } else {
                int h = k.threadLocalHashCode & (newLen - 1);
                while (newTab[h] != null)
                    h = nextIndex(h, newLen);
                newTab[h] = e;
                count++;
            }
        }
    }

    setThreshold(newLen);
    size = count;
    table = newTab;
}

基于逻辑完成,

get就比较简单了

先获取 Map ,然后根据 key 获取 value

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

关于ThreadLocal 内存溢出问题,一般只会出现在使用 线程池的时候,ThreadLocalMap 一直得不到释放,即使 key 因为软引用,但value没有释放造成的,但可以通过执行 remove 方法来主动释放

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java源码之ThreadLocal
详细介绍一下ThreadLocal是如何实现为线程提供变量副本的,方便下面源码的理解: 首先我们要知道每个线程下都有一个私有变量map,当我们使用ThreadLocal进行set(val)变量时,会向当前线程下的map中put一个键为当前ThreadLocal对象(虚引用),值为val的键值对,这样当使用ThreadLocal的get方法时,会直接向当前线程下的map获得键为此ThreadLocal的值。由于此操作只在当前线程下,所以完美的避免了并发
每天学Java
2020/06/01
4960
Java源码之ThreadLocal
用了这么久ThreadLocal,它的原理你还不懂吗
我们很多时候为了实现数据在线程级别下的隔离,会使用到ThreadLocal,那么TheadLocal是如何实现数据隔离的呢?今天就和大家一起分析一下ThreadLocal的实现原理。
Java进阶之路
2022/08/03
4380
用了这么久ThreadLocal,它的原理你还不懂吗
ThreadLocal
例如,以下类生成对每个线程唯一的局部标识符。 线程 ID 是在第一次调用 UniqueThreadIdGenerator.getCur
乐事
2020/05/10
5380
手把手系列——带你阅读ThreadLocal源码
在ThreadLocal中,有一个叫做ThreadLocalMap的静态内部类, 而这个类中有一个虚引用内部类Entry,Entry中有一个value,value存储的就是我们每一个线程的线程变量。而key,就是当前的threadlocal,在每一个线程进行创建的时候,会有一个init操作,在init的过程中会到用一个createMap方法,将父线程中的table给复制到当前线程中并且重新进行rehash,创建完后会赋值给当前线程对象(Thread)的threadLocals成员变量,在进行get的时候,会通过当前ThreadLocal一个内置的成员变量threadLocalHashCode来确定自身的entry在table中存储的slot位置,然后进行一个链表的遍历,对比ThreadLocal
Karos
2023/12/01
3111
手把手系列——带你阅读ThreadLocal源码
Thread、ThreadLocal源码解析
(1)首先看一下线程的构造方法,之后会说每种参数的用法,而所有的构造函数都会指向init方法
Liusy
2020/09/01
3980
Thread、ThreadLocal源码解析
线程安全和锁机制(四)谈谈 ThreadLocal 和 Handler
ThreadLocal可以实现线程本地存储的功能。把共享数据的可见范围限制在同一个线程内,就无须同步也能保证线程间不出现数据争用的问题。 那么它是如何实现解决数据争用的问题呢。看代码
提莫队长
2021/03/03
4140
ThreadLocal全面解析
ThreadLocal类保证了线程内部的变量在多线程环境下相对于其他线程是不可见的。
虞大大
2020/08/26
4470
ThreadLocal全面解析
ThreadLocal超深度源码解读,为什么要注意内存泄漏?不要道听途说,源码底下见真知!
在工作中,多线程访问同一个共享变量时存在并发问题,要么给这个共享变量加锁,要么将变量私有化,能不加锁就不加锁,ThreadLocal就是让每个线程访问自己的本地变量来避免并发问题。
100000860378
2021/04/15
4220
ThreadLocal超深度源码解读,为什么要注意内存泄漏?不要道听途说,源码底下见真知!
ThreadLocal学习笔记
Java项目中我们经常采用ThreadLocal作为session。显然ThreadLocal是线程隔离的。如果是线程隔离,那么ThreadLocal必然是线程的局部变量。在Thread类中我们记得有ThreadLocal相关的实体,如下图所示。
写一点笔记
2020/09/01
5020
ThreadLocal学习笔记
ThreadLocal到底有没有内存泄漏?从源码角度来剖析一波
ThreadLocal 也是一个使用频率较高的类,在框架中也经常见到,比如 Spring。
WriteOnRead
2020/07/10
7580
ThreadLocal 源码解析
ThreadLocal 顾名思义就是在每个线程内部都会存储只有当前线程才能访问的变量的一个副本,然后当前线程修改了该副本的值后而不会影响其他线程的值,各个变量之间相互不影响。
Java技术编程
2020/05/25
3910
ThreadLocal 核心源码解析
此类提供线程本地变量。这些变量与普通变量不同,因为每个访问一个变量(通过其get或set方法)的线程都有其自己的,独立初始化的变量副本。 ThreadLocal 实例通常是期望将状态与线程(例如,用户ID或事务ID)关联的类中的 private static 字段。
JavaEdge
2020/05/07
5390
ThreadLocal 核心源码解析
ThreadLocal Java多线程下的影分身之术
如果写过多线程的代码,你肯定考虑过线程安全问题,更进一步你可能还考虑在在线程安全的前提下性能的问题。大多数情况下大家用来解决线程安全问题都会使用同步,比如用synchron或者concurrent包提供的各种锁,当然这些都能解决问题。但有多线程做同步一定会涉及到资源争抢和等待的问题。java中各种同步方法都是提供一种准入机制,JVM会调用系统同步原语来保证临界区任意时刻只能有一个线程进入,那必然其他线程都得等待了,性能的瓶颈就在这同步上了。   解决问题最好的方式是啥?当然是避免问题的发生了。ThreadLocal就是用这样一种方式提升性能的。ThreadLocal遍历会为每个线程单独维护一份值,某个线程对其做任何操作都不会影响其他的线程,这相当于这个对象在每个线程下面都有了一个分身。ThreadLocal是以Thread为维度实现的,所以多线程之间也不会有争抢和等待,从而避免同步变成瓶颈,下文我们会从源码的维度去看这些都是如何实现的。   ThreadLocal也不是万金油,它也只能在多线程之间数据相互独立的情况下使用,如果是多线程间的数据同步,还得使用某个同步的方式。 我的理解,ThreadLocal是在临时变量完全不共享和全部变量完全共享之间取了个折中,在多线程数据一致的情况下完美的避免了资源争抢和等待,提高了性能。
xindoo
2021/01/22
4970
ThreadLocal Java多线程下的影分身之术
深入了解与使用ThreadLocal
ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()方法操作此变量,并且不会与其他线程的局部变量进行冲突,保证了线程间局部变量的隔离行。
Ant丶
2022/03/01
1.3K0
深入了解与使用ThreadLocal
ThreadLocal 源码分析
我们知道解决共享变量不安全的一种方式,就是利用每个线程的私有变量来操作,避免共享变量导致的线程不安全问题。
itliusir
2020/01/15
2950
【源码篇】ThreadLocal的奇思妙想(万字图文)
ThreadLocal的文章在网上也有不少,但是看了一些后,理解起来总感觉有绕,而且看了ThreadLocal的源码,无论是线程隔离、类环形数组、弱引用结构等等,实在是太有意思了!我必须也要让大家全面感受下其中所蕴含的那些奇思妙想! 所以这里我想写一篇超几儿通俗易懂解析ThreadLocal的文章,相关流程会使用大量图示解析,以证明:我是干货,不是水比!
小呆呆666
2021/05/06
8260
一文搞懂 ThreadLocal 原理
当多线程访问共享可变数据时,涉及到线程间同步的问题,并不是所有时候,都要用到共享数据,所以就需要线程封闭出场了。
武培轩
2020/04/08
5530
ThreadLocal 源码解读
ThreadLocal源码解读,网上面早已经泛滥了,大多比较浅,甚至有的连基本原理都说的很有问题,包括百度搜索出来的第一篇高访问量博文,说ThreadLocal内部有个map,键为线程对象,太误导人了。
java思维导图
2019/11/04
5180
ThreadLocal源码分析-黄金分割数的使用
最近接触到的一个项目要兼容新老系统,最终采用了ThreadLocal(实际上用的是InheritableThreadLocal)用于在子线程获取父线程中共享的变量。问题是解决了,但是后来发现对ThreadLocal的理解不够深入,于是顺便把它的源码阅读理解了一遍。在谈到ThreadLocal之前先买个关子,先谈谈黄金分割数。本文在阅读ThreadLocal源码的时候是使用JDK8(1.8.0_181)。
Throwable
2020/06/23
1.2K0
Java解读-ThreadLocal详解与应用
ThreadLocal 字面意思来看有点像“线程的本地实现版本”,实际上真正含义是ThreadLocalVariable(线程本地局部变量),所以把它命名为ThreadLocalVar更加合适。
高广超
2018/12/12
5960
相关推荐
Java源码之ThreadLocal
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验