惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Application and Cybersecurity Blog
Application and Cybersecurity Blog
G
GRAHAM CLULEY
P
Privacy International News Feed
L
LINUX DO - 热门话题
C
Cisco Blogs
T
Tor Project blog
AWS News Blog
AWS News Blog
K
Kaspersky official blog
C
Cybersecurity and Infrastructure Security Agency CISA
Cyberwarzone
Cyberwarzone
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
CERT Recently Published Vulnerability Notes
P
Proofpoint News Feed
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
Lohrmann on Cybersecurity
C
CXSECURITY Database RSS Feed - CXSecurity.com
Project Zero
Project Zero
Attack and Defense Labs
Attack and Defense Labs
Latest news
Latest news
The Last Watchdog
The Last Watchdog
Apple Machine Learning Research
Apple Machine Learning Research
Simon Willison's Weblog
Simon Willison's Weblog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Cloudbric
Cloudbric
Last Week in AI
Last Week in AI
S
Security Affairs
Google DeepMind News
Google DeepMind News
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
有赞技术团队
有赞技术团队
V
Visual Studio Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
小众软件
小众软件
P
Palo Alto Networks Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Security @ Cisco Blogs
V
V2EX
S
Secure Thoughts
人人都是产品经理
人人都是产品经理
月光博客
月光博客
博客园_首页
T
Troy Hunt's Blog
爱范儿
爱范儿
N
News and Events Feed by Topic
Hugging Face - Blog
Hugging Face - Blog
雷峰网
雷峰网
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
P
Privacy & Cybersecurity Law Blog
V
Vulnerabilities – Threatpost

青空之蓝

[青空之蓝-2023] - 色彩 | 青空之蓝 [青空之蓝-2022] - 平静 | 青空之蓝 [青空之蓝-2021] - 远望 | 青空之蓝 浅谈垃圾回收 | 青空之蓝 浅谈泛型擦除 | 青空之蓝 浅谈单点登录 | 青空之蓝 使用 Kotlin 编写 Spring 测试 | 青空之蓝 设计模式系列文章 | 青空之蓝 从零实现一个 Java 微框架 - IoC | 青空之蓝 从零实现一个 Java 微框架 - 前言 | 青空之蓝 浅谈 JVM:类加载 | 青空之蓝 浅谈 IO | 青空之蓝 浅谈并发:synchronized & ReentrantLock | 青空之蓝 浅谈并发:CAS & AQS | 青空之蓝 浅谈并发:三大特性 | 青空之蓝 浅谈组合注解 & 注解别名 | 青空之蓝 [青空之蓝-2020]-迷茫 | 青空之蓝 Java 系列文章 | 青空之蓝 HTTP 系列文章 | 青空之蓝 浅谈 EatWhatYouKill | 青空之蓝 浅谈可扩展线程池 | 青空之蓝 聊聊写框架 | 青空之蓝 聊聊现状-[2020-09] | 青空之蓝 浅谈并发:锁 | 青空之蓝 浅谈并发:基础 | 青空之蓝 浅谈缓存 | 青空之蓝 无须定义类,Spring 快速注入 Json 参数 | 青空之蓝 浅谈 Proxy 和 Aop | 青空之蓝 从零实现一个 PHP 微框架 - 初始化请求 | 青空之蓝 为 Vue3 添加一个简单的 Store | 青空之蓝 从零实现一个 PHP 微框架 - 服务提供者 | 青空之蓝 WSL2 踩坑记录 | 青空之蓝 浅谈浏览器Event Loop [更新] | 青空之蓝 从零实现一个 PHP 微框架 - Bootstrap 启动加载 | 青空之蓝 从零实现一个 PHP 微框架 - IoC 容器 | 青空之蓝 从零实现一个 PHP 微框架 - PSR & Composer | 青空之蓝 从零实现一个 PHP 微框架 - 前言 | 青空之蓝 MVVM 简单实现 | 青空之蓝 浅谈 DI 和 IoC | 青空之蓝 中间件实现 [PHP] | 青空之蓝 告别 Windows 终端的难看难用,打造好用的 PowerShell | 青空之蓝 VSCode Java输出中文乱码问题解决[更新] | 青空之蓝 浅谈浏览器渲染 | 青空之蓝 Vue-Cli@2 项目迁移日志 | 青空之蓝 Laragon & Scoop 集成踩坑记录 | 青空之蓝 「一行代码」优雅管理 Windows 软件 | 青空之蓝 [青空之蓝-2019]-年度总结 | 青空之蓝 为Vue添加简单的Store | 青空之蓝 为React添加简单的Store | 青空之蓝 为Vuex添加同步Action | 青空之蓝 浅谈B+树 | 青空之蓝 浅谈跳表 | 青空之蓝 浅谈数据库索引 | 青空之蓝 MySQL事务隔离 | 青空之蓝 算法复杂度分析(1) | 青空之蓝 一年来的经验总结 | 青空之蓝 Acrylic - VSCode Extension | 青空之蓝 ace编辑器设置惯性滚动 | 青空之蓝 Java二叉树实现 | 青空之蓝 Java图实现 | 青空之蓝 为apt方式安装的nginx重新编译增加WebDAV | 青空之蓝 XK-Editor - 一个支持富文本和Markdown的编辑器 | 青空之蓝 JS生成列表树 | 青空之蓝 Laravel生成目录树 | 青空之蓝 XK-Note - 集各种神奇功能的云笔记 | 青空之蓝 PHP GD生成验证码 | 青空之蓝 PHP GD图片处理[转换格式-水印-缩略图] | 青空之蓝 Origami - 简洁轻快的WordPress主题 | 青空之蓝 为WordPress启用WorkBox | 青空之蓝 [青空之蓝-2018]-年度总结 | 青空之蓝 VSCode Java手动导入jar和源码包 | 青空之蓝 Windows IP变化自动发送邮件 | 青空之蓝 C链表实现重制版 | 青空之蓝 C 结构体的定义和使用 | 青空之蓝 图的搜索(遍历) - BFS & DFS | 青空之蓝 Java链表实现 | 青空之蓝 C 快速排序 | 青空之蓝 C 插入排序 | 青空之蓝 C 归并排序 | 青空之蓝 C语言链表实现 | 青空之蓝 VSCode配置Java调试环境[Windows] | 青空之蓝 C 选择排序 | 青空之蓝 C 冒泡排序 | 青空之蓝 VSCode配置PHP调试环境[Windows] | 青空之蓝 VSCode配置C/C++ GDB调试环境[Windows] | 青空之蓝 WordPress友情链接模板 | 青空之蓝 Intel Optane 傲腾内存体验 | 青空之蓝 Mysql双机热备实战 | 青空之蓝 博客一年记录 | 青空之蓝 为WordPress启用Service Worker | 青空之蓝 Bing每日一图API | 青空之蓝 iframe延迟加载 | 青空之蓝 写在2018年高考前 | 青空之蓝 The Fox主题汉化分享 | 青空之蓝 [青空之蓝-2017]-崭新 | 青空之蓝 本博客评论规则 | 青空之蓝 世界,您好! | 青空之蓝
浅谈并发:ThreadLocal | 青空之蓝
Otstar Lin · 2021-02-12 · via 青空之蓝

前言

日常水文章.jpg

ThreadLocal

ThreadLocal 是关于创建线程局部变量的类,类似于沙箱,当前线程存储的变量只能被当前线程访问,不同线程间的变量是隔离开的。

ThreadLocal 其实只是一个委托类,实际存储的数据是存在线程中的 ThreadLocalMap 里,由于线程是互相隔离的,所以 Thread 里的数据也就原生隔离了。所以获取 ThreadLocal 的值其实经过了以下几个步骤:

  1. 首先获取当前线程。
  2. 利用当前线程作为句柄获取一个来自该实例中的 ThreadLocalMap 的对象。
  3. 如果上述 ThreadLocalMap 对象不为空,则从 ThreadLocalMap 中取得以当前 ThreadLocal 对象为 key 的值。
  4. 如果ThreadLocalMap 对象为空,或者取得的值为 null,则通过 initialValue 方法取得初始值,将初始值设置到 ThreadLocalMap 或者创建这个 ThreadLocalMap 对象并设置值。

源码如下:

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

getMap 方法其实就是获取了 t.threadLocals 这个属性,所以 ThreadLocal 里的值被对应的线程持有,存放于堆中。(当然这并不是绝对的,因为还有栈上分配、标量替换等优化)。

InheritableThreadLocal

除了 ThreadLocal 外,Java 中还提供了一个 InheritableThreadLocal

InheritableThreadLocal 是 ThreadLocal 的扩展版本,当父线程创建子线程的时候,父线程的 ThreadLocalMap 就会被复制到子线程中。这样就能做到线程间传递变量。

不过需要注意的是,发生传递的情况只有在子线程是父线程创建的,然而我们一般不会直接创建线程,而是通过线程池的方式来使用,这也导致了无法形成层级关系,此时父子线程间的上下文传递就没有用了。当然这也有解决方案,比较流行的如阿里的 TTL

ThreadLocalMap

ThreadLocalMap 虽有 Map 的后缀但是它并不继承自 Map 接口,所以内部的方法并没有同 Map 一样有非常多的方法。同时实现也不同于 HashMap,内部并没有使用 数组+链表(红黑树)的方式进行存储,而是只简单的 数组 来存储的。对于 hash 冲突的解决方式是采用开放寻址法

ThreadLocalMap 的 key 是弱引用,value 是强引用的存储结构。

内存泄漏

原因

首先说明一点,ThreadLocal 本身设计并不存在内存泄漏的问题,之所以会发生内存泄漏,实际上是因为错误的使用导致的。

由于 ThreadLocalMap 中的 key 是弱引用的,当 key 被 GC 清理后,ThreadLocalMap 中就只剩下了 value,而这个 value 是强引用的,会一直在线程中存在,此时即使 ThreadLocalMap 的使用者不再引用这些对象,这些对象也无法被垃圾回收,因为还有一条引用链引用这这个 value

如果没有调用 remove 方法清除不再需要使用的值,那么这个值就会一直存在直到 Thread 对象被销毁。然而我们一般情况下都是使用线程池,所以线程的生命周期非常长,不 remove 最终可能会导致 OOM。

解决方案

对于过期的值清除有以下三种方式:

  1. **显示清除:**这是推荐的方式,通过调用 remove 方法就可以将下面那条引用断开,这样 value 就不再拥有强引用了,GC 也就能回收这个对象了。
  2. **隐式清除:**隐式清除分为两个步骤:
    • **清除 key:**由于 Key 是弱引用的,其指向的 ThreadLocal 实例可被 GC 回收,回收后从 k-v 变成 null-v
    • **清除 value:**当我们调用 getset 等方法的时候,会自动寻找 key 为 null 的元素并删除(expungeStaleEntry)。
  3. **自动清除:**由于 ThreadLocalMap 是随着 Thread 存在的,当 Thread 被回收的时候,ThreadLocalMap 就会被一起回收。这在使用线程池的情况下基本无法使用。

为了避免发生保持在 null-v 这种内存泄漏的情况,我们可以使用显示清除这种较为简单也是最推荐的方式。

或者也可以将 ThreadLocal 设置为 static 这种情况,这样就保证了 key 不会被 GC 自动回收,当我们再次使用的时候就会覆盖之前的值,之前的值也就不存在强引用了。但是这种方式同样也要注意一点,由于我们一般会使用线程池来执行任务,那么 ThreadLocalMap 也会被保留下来,其中的值不会被清除,当我们再次使用 ThreadLocal 的时候,如果未先 remove 掉旧值那么就有可能造成值残留的问题。

结语

总算写完文章了,差不多写了快 2 小时吧,虽然不是什么复杂的知识,但是耗时确实挺多的。

另外吐槽下这个坑爹的 WordPress,越更新越难用,现在这个 Gutenberg 编辑器越来越卡,打算换平台了,但是又不舍得这个主题(换平台移植主题太花时间了,今年估计是没法)。

浅谈并发:ThreadLocal

https://blog.ixk.me/post/talking-about-concurrency-threadlocal
  • 许可协议

    BY-NC-SA

  • 本文作者

    Otstar Lin

  • 发布于

    2021/02/12

转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!

浅谈并发:CAS & AQS浅谈并发:三大特性