ThreadLocal 的使用方法

static class SafeDateFormat {// 定义 ThreadLocal 变量static final ThreadLocal<DateFormat>tl = ThreadLocal.withInitial(()-> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));static DateFormat get(){return tl.get();}
}// 不同线程执行下面代码
// 返回的 df 是不同的
DateFormat df = SafeDateFormat.get();

ThreadLocal 的工作原理

错误的实现方案

class MyThreadLocal<T> {Map<Thread, T> locals = new ConcurrentHashMap<>();// 获取线程变量 T get() {return locals.get(Thread.currentThread());}// 设置线程变量void set(T t) {locals.put(Thread.currentThread(), t);}
}

JDK实现方案

Thread 这个类内部有一个私有属性 threadLocals,其类型就是 ThreadLocalMap,ThreadLocalMap 的 Key 是ThreadLocal。

class Thread {// 内部持有 ThreadLocalMapThreadLocal.ThreadLocalMap threadLocals;
}class ThreadLocal<T>{public T get() {// 首先获取线程持有的//ThreadLocalMapThreadLocalMap map = Thread.currentThread().threadLocals;// 在 ThreadLocalMap 中// 查找变量Entry e = map.getEntry(this);return e.value; }static class ThreadLocalMap{// 内部是数组而不是 MapEntry[] table;// 根据 ThreadLocal 查找 EntryEntry getEntry(ThreadLocal key){// 省略查找逻辑}//Entry 定义static class Entry extends WeakReference<ThreadLocal>{Object value;}}
}

ThreadLocal 仅仅是一个代理工具类,内部并不持有任何与线程相关的数据,所有和线程相关的数据都存储在 Thread 里面,这样的设计容易理解。而从数据的亲缘性上来讲,ThreadLocalMap 属于 Thread 也更加合理。

当然还有一个更加深层次的原因,那就是不容易产生内存泄露。在我们的设计方案中,ThreadLocal 持有的 Map 会持有 Thread 对象的引用,这就意味着,只要 ThreadLocal对象存在,那么 Map 中的 Thread 对象就永远不会被回收。ThreadLocal 的生命周期往往都比线程要长,所以这种设计方案很容易导致内存泄露。而 Java 的实现中 Thread 持有ThreadLocalMap,而且 ThreadLocalMap 里对 ThreadLocal 的引用还是弱引用(WeakReference),所以只要 Thread 象可以被回收,那么 ThreadLocalMap 就能被回收。Java 的这种实现方案虽然看上去复杂一些,但是更加安全。

ThreadLocal 与内存泄露

在线程池中使用 ThreadLocal 为什么可能导致内存泄露呢?原因就出在线程池中线程的存活时间太长,往往都是和程序同生共死的,这就意味着 Thread 持有的 ThreadLocalMap一直都不会被回收,再加上 ThreadLocalMap 中的 Entry 对 ThreadLocal 是弱引用(WeakReference),所以只要 ThreadLocal 结束了自己的生命周期是可以被回收掉的。但是 Entry 中的 Value 却是被 Entry 强引用的,所以即便 Value 的生命周期结束了,Value 也是无法被回收的,从而导致内存泄露。

那在线程池中,我们该如何正确使用 ThreadLocal 呢?其实很简单,既然 JVM 不能做到自动释放对 Value 的强引用,那我们手动释放就可以了。

ExecutorService es;
ThreadLocal tl;
es.execute(()->{//ThreadLocal 增加变量tl.set(obj);try {// 省略业务逻辑代码}finally {// 手动清理 ThreadLocal tl.remove();}
});

InheritableThreadLocal 与继承性

通过 ThreadLocal 创建的线程变量,其子线程是无法继承的。也就是说你在线程中通过
ThreadLocal 创建了线程变量 V,而后该线程创建了子线程,你在子线程中是无法通过
ThreadLocal 来访问父线程的线程变量 V 的。

如果你需要子线程继承父线程的线程变量,那该怎么办呢?其实很简单,Java 提供了
InheritableThreadLocal 来支持这种特性。

不过,不建议你在线程池中使用 InheritableThreadLocal,不仅仅是因为它具有ThreadLocal 相同的缺点——可能导致内存泄露,更重要的原因是:线程池中线程的创建是动态的,很容易导致继承关系错乱,如果你的业务逻辑依赖 InheritableThreadLocal,那么很可能导致业务逻辑计算错误,而这个错误往往比内存泄露更要命。

Java并发编程实战~ThreadLocal相关推荐

  1. Java并发编程实战_不愧是领军人物!这种等级的“Java并发编程宝典”谁能撰写?...

    前言 大家都知道并发编程技术就是在同一个处理器上同时的去处理多个任务,充分的利用到处理器的每个核心,最大化的发挥处理器的峰值性能,这样就可以避免我们因为性能而产生的一些问题. 大厂的核心负载肯定是非常 ...

  2. java单线程共享,「Java并发编程实战」之对象的共享

    前言 本系列博客是对<Java并发编程实战>的一点总结,本篇主要讲解以下几个内容,内容会比较枯燥.可能大家看标题不能能直观的感受出到底什么意思,这就是专业术语,哈哈,解释下,术语(term ...

  3. 视频教程-Java并发编程实战-Java

    Java并发编程实战 2018年以超过十倍的年业绩增长速度,从中高端IT技术在线教育行业中脱颖而出,成为在线教育领域一匹令人瞩目的黑马.咕泡学院以教学培养.职业规划为核心,旨在帮助学员提升技术技能,加 ...

  4. java并发编程实战(二)

    java并发编程中常常会用到两种容器来存放一些数据,这些数据需要保证能在多线程下正常访问.常见的容器分为两类:同步容器和并发容器.在java并发编程实战一书中的第五章也有讲解. 什么是同步容器以及优劣 ...

  5. JAVA并发编程实战——共享对象

    目录 思维导图 1. 可见性 1. 1 过期数据 1.2 锁和可见性 1.3 Volatile变量 2. 发布和逸出 2.1 安全构建实践 3. 线程封闭 3.1 栈限制 3.2 ThreadLoca ...

  6. 《Java 并发编程实战》--读书笔记

    Java 并发编程实战 注: 极客时间<Java 并发编程实战>–读书笔记 GitHub:https://github.com/ByrsH/Reading-notes/blob/maste ...

  7. Java并发编程实战笔记2:对象的组合

    设计线程安全的类 在设计现车让安全类的过程之中,需要包含以下三步: 找出构成对象状态的所有变量 找出约束状态变量的不变性条件 建立对象状态的并发访问策略 实例封闭 通过封闭机制与合适的加锁策略结合起来 ...

  8. aqs clh java_【Java并发编程实战】—– AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形. 其主要从双方面进行了改造:节点的结构与节点等待机制.在结构上引入了 ...

  9. java 多线程缓存_[Java教程]【JAVA并发编程实战】12、使用condition实现多线程下的有界缓存先进先出队列...

    [Java教程][JAVA并发编程实战]12.使用condition实现多线程下的有界缓存先进先出队列 0 2016-11-29 17:00:10 package cn.study.concurren ...

最新文章

  1. 洛谷 P1019 单词接龙 Label:dfs
  2. VS2005+cygwin编译WebKit
  3. 机器学习算法-异常值检测(outlier),30 亿个雅虎账户被黑客入侵?
  4. Android Widevine 基本概念
  5. 相见恨晚的一款前端布局神器!
  6. Token是什么 Token登录认证
  7. 北京邮电大学计算机学院考研夏令营,北京邮电大学计算机学院(专业学位)计算机技术保研夏令营...
  8. 关于header file、static、inline、variable hides的一点感想
  9. Caused by: java.lang.IllegalStateException: Failed to introspect Class
  10. Windows起一个Docker镜像——起起起起起~不~来~
  11. java 二进制最大值_java int型最大值/最小值,最大值+1,最小值-1
  12. 给定一个单词,如果该单词以er、ly或者ing后缀结尾, 则删除该后缀(逐句精解)
  13. html传递汉字参数转换,url中如何传递中文信息呢?
  14. (每日水题004-1)(DP,滚动数组)洛谷P1508 Likecloud-吃、吃、吃
  15. 自动化智能立体库是如何进行仓储工作的?自动化立体库用WMS/WCS系统方案!
  16. python字典相乘_知乎
  17. 软件安全理论测试部分
  18. 一招去除迅雷客户端首页视频 斩掉流氓特性 回归下载本质
  19. 计算机打不开找不到应用程序,win10系统打不开压缩包提示“找不到应用程序的方法...
  20. 2016.7.14比赛总结

热门文章

  1. 蚂蚁中间件团队Java面试题:Netty+Redis+Kafka+MongoDB+分布式
  2. 神经网络不应视为模型,推理过程当为机器学习问题一等公民
  3. 深入解析Node.js setTimeout方法的执行过程
  4. IDEA:将web工程部署到tomcat
  5. 万恶之源 - Python运算符与编码
  6. [COCI2017-2018#1] Plahte
  7. Pycharm简单配置及详细快捷键介绍
  8. exit(0)和exit(1)区别
  9. JavaScript,等比例缩放图片的函数,很好用。
  10. 00003-回文数的判定-leetcode-解法不唯一,1.reverse最简单,2.数学方法很有意思