文档章节

ThreadLocal源码阅读

小海bug
 小海bug
发布于 2019/12/07 22:48
字数 602
阅读 67
收藏 0

首先,从set方法入手,

    // 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

首次创建

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 采用弱引用,在内存不足时候,会被回收

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> 数组 

如果已存在,

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();
}

扩容

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

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 方法来主动释放

© 著作权归作者所有

小海bug

小海bug

粉丝 25
博文 136
码字总数 76649
作品 0
北京
架构师
私信 提问
工作5年的Java程序员,才学会阅读源码,可悲吗?

最近一位5年开发经验的群友与我聊天 他说:最近慢慢的尝试去看spring的源码,学习spring,以前都只是会用就行了,但是越是到后面,发现只懂怎么用还不够,在面试的时候经常被问到一些开源框架...

Java架构
2019/02/14
0
0
sharding-jdbc源码分析—准备工作

原文作者:阿飞Javaer 原文链接:https://www.jianshu.com/p/7831817c1da8 接下来对sharding-jdbc源码的分析基于tag为源码,根据sharding-jdbc Features深入学习sharding-jdbc的几个主要特性...

飞哥-Javaer
2018/05/03
0
0
高手问答第 217 期 —— 如何进阶 Java 并发编程?

OSCHINA 本期高手问答(2018 年 11 月 14 日 — 11 月 20 日)我们请来了@技术原始积累 翟陆续(加多)为大家解答关于 Java 并发编程方面的问题。 翟陆续(加多),资深 Java 开发工程师,著有...

局长
2018/11/13
1.5W
47
ThreadLocal源码分析

阅读原文请访问我的博客 BrightLoong's Blog 一. 简介 提醒篇幅较大需耐心。 简介来自ThreadLocal类注释 ThreadLocal类提供了线程局部 (thread-local) 变量。这些变量与普通变量不同,每个线...

BrightLoong
2018/05/28
0
0
我是如何通过学习拿到年薪80w

本人做了5年Java,如今还是一个码农,天天写业务代码,直到2018年11月中旬遇到一位技术大牛,他给我一套技术思维导图让我又看到了希望!今天分享给各位想要提升、升职、加薪的你。 普通Java程...

架构说
2018/11/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

每天AC系列(六):有效的括号

1 题目 LeetCode第20题,这题比较简单,匹配括号. 2 栈 这是栈的典型应用,括号匹配,当然不需要直接使用栈,使用一个StringBuilder即可: if(s.isEmpty()) return true;char a = s.charAt(0);...

Blueeeeeee
今天
27
0
Spring AOP-06-切入点类型

切入点是匹配连接点的拦截规则。之前使用的是注解@Pointcut,该注解是AspectJ中的。除了这个注解之外,Spring也提供了其他一些切入点类型: • 静态方法切入点StaticMethodMatcherPointcut •...

moon888
昨天
90
0
Class Loaders in Java

1. Introduction to Class Loaders Class loaders are responsible for loading Java classes during runtime dynamically to the JVM (Java Virtual Machine). Also, they are part of the ......

Ciet
昨天
96
0
以Lazada为例,看电商系统架构演进

什么是Lazada? Lazada 2012年成立于新加坡,是东南亚第一电商,2016年阿里投资10亿美金,2017年完成对lazada的收购。 业务模式上Lazada更偏重自营,类似于亚马逊,自建仓储和为商家提供服务...

春哥大魔王的博客
昨天
62
0
【自用】 Flutter Timer 简单用法

dart: void _startTime() async { _timer = Timer(Duration(seconds: sec), () { fun(xxx,yyy,zzz); }); } @override void dispose() { _timer.cancel()......

Tensor丨思悟
昨天
65
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部