一、引入

public class Thread implements Runnable {
/* 前面略 */ /* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/* 后面略 */
}

首先我们看到的是 Thread 中有一个属性 threadLocals,它的类型是 ThreadLocalMap,封装类型是 default(表示它只能在包内可见),jdk 是这么介绍它的:与此线程有关的 ThreadLocal 值,该映射由 ThreadLocal 类维护。 啥意思呢?那就来看看 ThreadLocalMap 是啥玩意!

public class ThreadLocal<T> {
/* 前面略 */ static class ThreadLocalMap { /**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value; Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/* 后面略 */
}
}

从类定义上可以看出 ThreadLocal 是支持泛型的,而 ThreadLocalMap 是 ThreadLocal 的一个内部类,封装类型也是 default(表示只能在包内可见),jdk 是这么介绍它的:ThreadLocalMap 是自定义的哈希映射,仅适用于维护线程局部值。并且为了存储容量可控,不至于内存泄漏,哈希表条目使用弱引用作为键(弱引用的对象的生命周期直到下一次垃圾回收之前被回收),ThreadLocalMap 使用静态内部类 Entry(可以类比 Map 中的 entry)来存储实际的 key 和 value。

从上面这些介绍,我们可以大致想到,ThreadLocal 是一个与线程相关的类,用来存储维护线程局部值。

二、set(T value) 方法解读

    public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
    ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
    void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

可以看到 set(T value) 方法做的事情很简单,就是维护 Thread 的 threadLocals 属性,如果该属性不存在的话,就以当前 ThreadLocal 实例为 key 创建一个;该属性存在的话,则直接赋值。

三、get() 方法解读

    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();
}
    private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
    protected T initialValue() {
return null;
}

get() 的方法同样很简单,就是从 Thread 的 threadLocals 属性获取值,如果获取不到,则把 initialValue() 的值赋值给线程的 threadLocals 属性并返回。initialValue() 方法是一个 protected 类型的方法,默认返回 null,我们可以在创建 ThreadLocal 的时候重写它,表示所有线程的默认值。

    // java8 的方式
ThreadLocal<Boolean> threadLocal1 = ThreadLocal.withInitial(() -> false);
//
ThreadLocal<Boolean> threadLocal2 = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return false;
}
};

四、总结

  • ThreadLocal 用于存储维护线程的局部值。
  • 和 ThreadLocal 类似的还有一个叫 InheritableThreadLocal, InheritableThreadLocal 继承自 ThreadLocal,用于父子线程间共享共同的值,父线程中设置的值,子线程中可以访问到。
  • 上面看 ThreadLocalMap 的时候,我们知道 key 是弱引用,gc 的时候 key 会被回收,但是 value 和 ThreadLocalMap 的引用不会被回收,如果这种情况的 Thread 很多,而且一直没有执行完,就可能会出现内存泄漏,因此使用完 ThreadLocal 的时候尽量调用 ThreadLocal 的 remove() 方法。
  • 当使用线程池的时候,在调用 ThreadLocal 的 set 方法后,却没有调用 remove 的方法,如果同一个线程再去调用 get 方法可能拿到的值并不是当时 set 进去的(因为线程池的线程是复用的),可能导致程序数据异常之类的,因此使用完 ThreadLocal 的时候尽量调用 ThreadLocal 的 remove() 方法。

ThreadLocal 源码解读的更多相关文章

  1. ThreadLocal源码解读

    1. 背景 ThreadLocal源码解读,网上面早已经泛滥了,大多比较浅,甚至有的连基本原理都说的很有问题,包括百度搜索出来的第一篇高访问量博文,说ThreadLocal内部有个map,键为线程对象 ...

  2. 线程本地变量ThreadLocal源码解读

      一.ThreadLocal基础知识 原始线程现状: 按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用synchronized进行线程同步.但是Spring中的各种模板 ...

  3. 对ThreadLocal的源码解读

    早在JDK 1.2的版本中就提供Java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编写出优美的多线程程序. 功能 ...

  4. SpringCloud之RefreshScope 源码解读

    SpringCloud之RefreshScope @Scope 源码解读 Scope(org.springframework.beans.factory.config.Scope)是Spring 2. ...

  5. Netty异步Future源码解读

    本文地址: https://juejin.im/post/5df771ee6fb9a0161d743069 说在前面 本文的 Netty源码使用的是 4.1.31.Final 版本,不同版本会有一些差 ...

  6. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  7. SDWebImage源码解读 之 NSData+ImageContentType

    第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...

  8. SDWebImage源码解读 之 UIImage+GIF

    第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...

  9. SDWebImage源码解读 之 SDWebImageCompat

    第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...

随机推荐

  1. 分类算法之逻辑回归(Logistic Regression

    分类算法之逻辑回归(Logistic Regression) 1.二分类问题 现在有一家医院,想要对病人的病情进行分析,其中有一项就是关于良性\恶性肿瘤的判断,现在有一批数据集是关于肿瘤大小的,任务就 ...

  2. Linux | 性能分析系列学习 (2)

    常分析方法: 1.监控大盘,是否异常报警 2..平均负载情况,(top    /    htop )平均负载体现的是系统的一个整体情况,他应该是cpu.内存.磁盘性能的一个综合,一般是平均负载的值大于 ...

  3. mjpg-stream 视频服务 (1)| 简介与配置树莓派使用

    源码地址为:https://github.com/jacksonliam/mjpg-streamer Mjpg简介: (1)mjpg-streamer是一个命令行应用程序,它将JPEG帧从一个或多个输 ...

  4. Vue基础系列(二)——Vue中的methods属性

      写在前面的话: 文章是个人学习过程中的总结,为方便以后回头在学习. 文章中会参考官方文档和其他的一些文章,示例均为亲自编写和实践,若有写的不对的地方欢迎大家指出. 作者简介: 一个不知名的前端开发 ...

  5. 小白历险记:spingboot之helloworld

    还记得入职第一天的时候,先安装了相关的软件,配置了环境.boss叫我写的第一个程序:搭建一个springboot工程,输出helloworld. 哈哈话不多说,回忆一下. 1.打开IDEA,点击Cre ...

  6. .NET Core 3.0 单元测试与 Asp.Net Core 3.0 集成测试

    单元测试与集成测试 测试必要性说明 相信大家在看到单元测试与集成测试这个标题时,会有很多感慨,我们无数次的在实践中提到要做单元测试.集成测试,但是大多数项目都没有做或者仅建了项目文件.这里有客观原因, ...

  7. 红帽7.4(RHCE7.4)磁盘扩容详细步骤

    参照博文VMware虚拟机CentOS 7 磁盘扩容:https://www.linuxidc.com/Linux/2019-04/158346.htm 01.虚拟机扩容磁盘.如下图 02.使用roo ...

  8. ReactJS的4行代码

    Angular 2一个显著的变动是,把Angular 1的Promise pattern改成了Observer pattern,并且使用了ReactJS.这里有一篇值得一读的文章 要搞懂ReactJS ...

  9. Python之selenium自动化PART1

    本文适合有经验的测试童鞋 一.Selenium自动化测试环境搭建 1.cmd --- pip install selenium==2.53.0 (如果selenium后面不跟==,表示默认安装最新版本 ...

  10. Charles 笔记

    一. Charles工作原理 本质就是一个http抓包分析工具,在工作的时候将charles设置成代理服务器,所有网络请求都会经过Charles,这样就实现了网络封包的截取和分析. 主要功能 截取ht ...