博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java ThreadLocal 源码解读
阅读量:4093 次
发布时间:2019-05-25

本文共 2929 字,大约阅读时间需要 9 分钟。

ThreadLocal 源码解读

  • 之前对于ThreadLocal的用法和原理都没有深入的理解,因此准备学习一番有关于ThreadLocal的知识。

ThreadLocal是什么?

  • 它为线程提供了线程本地的变量,每个线程都保存有一个变量副本,使得同一个线程在任何时刻访问它的时候都是一致的。它的生命周期跟线程绑定在一起,线程结束生命周期,该变量副本也会被GC。简单说 threadlocal提供了一个线程隔离与线程绑定。通常定义为 private static 类型

主要变量及方法 ##

  • 变量
    • threadLocalHashCode
private final int threadLocalHashCode = nextHashCode();
  • 通过此变量保证threadLocal的唯一性,并通过CAS操作更新,增量为 HASH_INCREMENT = 0x61c88647
  • 方法
  • 构造函数
    • ThreadLocal threadLocal = new ThreadLocal<>();
  • set()
public void set(T value) {        // 获取当前线程        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else        // map为空则创建            createMap(t, value);    }    // thread 中保存实例成员变量tl    ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }    void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }
  • threadLocalMap
    • Entry[] table // entry数组 用于存储数据 Entry(ThreadLocal
private static int nextIndex(int i, int len) {            return ((i + 1 < len) ? i + 1 : 0);        }
  • cleanSomeSlots清理
  • 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();    }
  • ThreadLocalMap.getEntry()
    • hash以后如果是ThreadLocal对应的Entry就返回,没有找到就调用getEntryAfterMiss()线性探测寻找 return entry || null
  • remove()
public void remove() {        // 获取当前线程维护的threadlocalmap         ThreadLocalMap m = getMap(Thread.currentThread());         if (m != null)             m.remove(this);     }    private void remove(ThreadLocal
key) { 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)]) { if (e.get() == key) { e.clear(); // remove的时候同样也会调用expungeStaleEntry方法执行清理工作 expungeStaleEntry(i); return; } } }

小结

  • 每个Thread里都含有一个ThreadLocalMap的成员变量,这种机制将ThreadLocal和线程巧妙地绑定在了一起,即可以保证无用的ThreadLocal被及时回收,不会造成内存泄露,又可以提升性能。
  • 假如我们把ThreadLocalMap做成一个Map,那么它存储的东西将会非常多(相当于一张全局线程本地变量表),这样的情况下用线性探测法解决哈希冲突的问题效率会非常差。而JDK里的这种利用ThreadLocal作为key,再将ThreadLocalMap与线程相绑定的实现,完美地解决。
  • 关于GC
    • 无用 Entry 什么时候会被清理
      • 线程结束
      • 调用ThreadLocalMap的remove方法或set(null)时
      • 插入元素时,发现staled entry,则会进行替换并清理
      • 插入元素时,map大小达到阈值,并且没有任何staled entries的时候,会调用rehash方法清理并扩容
  • 应用场景
    • 实现单个线程单例以及单个线程上下文信息存储,比如交易id等
    • 实现线程安全,非线程安全的对象使用ThreadLocal之后就会变得线程安全,因为每个线程都会有一个对应的实例
    • 承载一些线程相关的数据,避免在方法中来回传递参数
    • 在线程池中 Thread不关闭 一般都会调用remove移除
  • 空间换时间思想
  • InheritableThreadLocal 能够复制父类的数据
  • 判断内存泄露的标准应该是 一个对象不再需要了,却无法回收依然存在才可以称之为内存泄漏,
  • 参考

转载地址:http://whiii.baihongyu.com/

你可能感兴趣的文章
漫谈一下前端的可视化技术
查看>>
VUe+webpack构建单页router应用(一)
查看>>
Vue+webpack构建单页router应用(二)
查看>>
从头开始讲Node.js——异步与事件驱动
查看>>
Node.js-模块和包
查看>>
Node.js核心模块
查看>>
express的应用
查看>>
NodeJS开发指南——mongoDB、Session
查看>>
Express: Can’t set headers after they are sent.
查看>>
2017年,这一次我们不聊技术
查看>>
实现接口创建线程
查看>>
Java对象序列化与反序列化(1)
查看>>
HTML5的表单验证实例
查看>>
JavaScript入门笔记:全选功能的实现
查看>>
程序设计方法概述:从面相对象到面向功能到面向对象
查看>>
数据库事务
查看>>
JavaScript基础1:JavaScript 错误 - Throw、Try 和 Catch
查看>>
SQL基础总结——20150730
查看>>
SQL join
查看>>
JavaScript实现页面无刷新让时间走动
查看>>