一、引入

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() 方法。

最新文章

  1. Dagger2系列之使用方法
  2. elk的搭建----待续
  3. SQL*Loader之CASE7
  4. squid清除缓存
  5. 刷连记录的迟到检测---Table表格增加一列值
  6. Java语法基础(一)----关键字、标识符、常量、变量
  7. WPF多线程UI更新——两种方法
  8. .Net程序猿乐Android发展---(1)环境结构
  9. 联想昭阳e43l笔记本配置
  10. mui开发app之cropper裁剪后上传头像的实现
  11. java模拟一个抽奖程序
  12. PHP 页面跳转到另一个页面的多种方法方法总结
  13. C# 委托链(多播委托)
  14. python flsak 框架
  15. Python 30分钟快速入门指南
  16. 树——B-树
  17. Linux系统下DNS主从配置详解
  18. JDBC - Mysql 8.0.13 连接测试
  19. 在ASP.NET MVC下扩展一个带验证的RadioButtonList
  20. 《课程设计》——cupp的使用

热门文章

  1. Ubuntu18.04 安装MySQL(Linux)解决登陆权限问题及Navicat for mysql 中文乱码问题
  2. NOIP模拟 25
  3. 程序员学点xx 之 Redis
  4. js 重写a标签的href属性和onclick事件
  5. [UWP]为番茄钟应用设计一个平平无奇的状态按钮
  6. 01-MyBatis启动流程分析
  7. Docker 资源 | 官方文件
  8. python之小木马(文件上传,下载,调用命令行,按键监控记录)
  9. docker项目——上线tomcat网站
  10. 使用vue-cli搭建项目开发环境