今天在看Spring 3.x企业应用开发实战,第九章 Spring的事务管理,9.2.2节ThreadLocal的接口方法时,书上有提到Threadlocal的简单实现,我就去看了下JDK1.8的Threadlocal的源码。发现实现方式与书中讲的并不相同,同时在网上搜索了一下,发现有比较多的人理解错了。

先看一下容易误导的解释:在ThreadLocal类中有一个Map对象,这个Map以每个Thread对象为键,保存了这个线程对应局部变量值,对应的实现方式如下:

  1. public class SimpleThreadLocal {
  2. private Map valueMap = Collections.synchronizedMap(new HashMap());
  3. public void set(Object newValue) {
  4. valueMap.put(Thread.currentThread(), newValue);//①键为线程对象,值为本线程的变量副本
  5. }
  6. public Object get() {
  7. Thread currentThread = Thread.currentThread();
  8. Object o = valueMap.get(currentThread);//②返回本线程对应的变量
  9. if (o == null && !valueMap.containsKey(currentThread)) {//③如果在Map中不存在,放到Map 中保存起来。
  10. o = initialValue();
  11. valueMap.put(currentThread, o);
  12. }
  13. return o;
  14. }
  15. public void remove() {
  16. valueMap.remove(Thread.currentThread());
  17. }
  18. public Object initialValue() {
  19. return null;
  20. }
  21. }

为什么不按照那种有误的方法实现呢?

看起来似乎是不同线程获取了各自的值,但是这些值并没有线程独立。线程A可以操作线程B对应的值。如果某个线程将保存这些值的Map置为null了,那么其他线程也无法访问了。

实际上是怎样的呢

我们看ThreadLocal的get()方法源码。可以看到获取Threadlocal中的值,是先通过当前线程的线程对象t,获取t的ThreadlocalMap属性对象,然后再以Threadlocal对象为键,去获取ThreadlocalMap中的值。

  1. public T get() {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);//获取线程对象的属性
  4. if (map != null) {
  5. ThreadLocalMap.Entry e = map.getEntry(this);//这里的this是指Threadlocal对象,Threadlocal在类中通常以静态属性出现,所以多个线程的Threadlocal指向同一个对象。
  6. if (e != null) {
  7. @SuppressWarnings("unchecked")
  8. T result = (T)e.value;
  9. return result;
  10. }
  11. }
  12. return setInitialValue();
  13. }
  14. ThreadLocalMap getMap(Thread t) {
  15. return t.threadLocals;
  16. }

同时我们查看ThreadLocal源码中定义的静态类ThreadLocalMap,其实底层封装的是一个Entry数组,获取方式和普通的HashMap不太一样,如果没有命中,就直接通过线性搜索,因为ThreadLocalMap需要保存的Entry并不会太多。

  1. private Entry getEntry(ThreadLocal<?> key) {
  2. int i = key.threadLocalHashCode & (table.length - 1);
  3. Entry e = table[i];
  4. if (e != null && e.get() == key)
  5. return e;
  6. else
  7. return getEntryAfterMiss(key, i, e);
  8. }
  9. private void set(ThreadLocal<?> key, Object value) {
  10. // We don't use a fast path as with get() because it is at
  11. // least as common to use set() to create new entries as
  12. // it is to replace existing ones, in which case, a fast
  13. // path would fail more often than not.
  14. Entry[] tab = table;
  15. int len = tab.length;
  16. int i = key.threadLocalHashCode & (len-1);
  17. for (Entry e = tab[i];
  18. e != null;
  19. e = tab[i = nextIndex(i, len)]) {
  20. ThreadLocal<?> k = e.get();
  21. if (k == key) {
  22. e.value = value;
  23. return;
  24. }
  25. if (k == null) {
  26. replaceStaleEntry(key, value, i);
  27. return;
  28. }
  29. }

通过ThreadLocal,每个线程保存自身的数据,不能访问到其他线程的数据。

ThreadLocalMap的Entry使用ThreadLocal的WeakReference引用作为Key值,当所有线程运行出ThreadLocal的作用域时,即没有强引用ThreadLocal时,ThreadLocal就会被回收。

  1. static class Entry extends WeakReference<ThreadLocal<?>> {
  2. /** The value associated with this ThreadLocal. */
  3. Object value;
  4. Entry(ThreadLocal<?> k, Object v) {
  5. super(k);
  6. value = v;
  7. }
  8. }

不是Threadlocal为每个线程提供了独立的变量,而是每个线程自己带了自己独立的变量。

关于内存泄漏

关于ThreadLocalMap的内存泄漏:如果一个ThreadLocal的生命周期结束,即在ThreadLocal所处的类中没有了强引用,而Thread没有结束,在Thread的threadLocals成员变量中,会有一个Entry使用弱引用引用了ThreadLocal作为key,因为是弱引用,这个key将被回收。而value是强引用,看起来是会造成泄漏,但是在ThreadLocalMap的set和get方法中,有一些释放的方法。具体的我也不太懂。

还是老老实实使用ThreadLocal的remove方法比较好。

  1. public void remove() {
  2. ThreadLocalMap m = getMap(Thread.currentThread());
  3. if (m != null)
  4. m.remove(this);
  5. }

Threadlocal源码分析以及其中WeakReference作用分析的更多相关文章

  1. Java并发编程之ThreadLocal源码分析

    ## 1 一句话概括ThreadLocal<font face="微软雅黑" size=4>  什么是ThreadLocal?顾名思义:线程本地变量,它为每个使用该对象 ...

  2. 并发编程(四)—— ThreadLocal源码分析及内存泄露预防

    今天我们一起探讨下ThreadLocal的实现原理和源码分析.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用需要注意的地方,最后给出了两 ...

  3. ThreadLocal源码及相关问题分析

    前言 在高并发的环境下,当我们使用一个公共的变量时如果不加锁会出现并发问题,例如SimpleDateFormat,但是加锁的话会影响性能,对于这种情况我们可以使用ThreadLocal.ThreadL ...

  4. ThreadLocal源码分析-黄金分割数的使用

    前提 最近接触到的一个项目要兼容新老系统,最终采用了ThreadLocal(实际上用的是InheritableThreadLocal)用于在子线程获取父线程中共享的变量.问题是解决了,但是后来发现对T ...

  5. Java多线程学习之ThreadLocal源码分析

    0.概述 ThreadLocal,即线程本地变量,是一个以ThreadLocal对象为键.任意对象为值的存储结构.它可以将变量绑定到特定的线程上,使每个线程都拥有改变量的一个拷贝,各线程相同变量间互不 ...

  6. ThreadLocal详解,ThreadLocal源码分析,ThreadLocal图解

    本文脉路: 概念阐释 ---->  原理图解  ------> 源码分析 ------>  思路整理  ----> 其他补充. 一.概念阐述. ThreadLocal 是一个为 ...

  7. 【JAVA】ThreadLocal源码分析

    ThreadLocal内部是用一张哈希表来存储: static class ThreadLocalMap { static class Entry extends WeakReference<T ...

  8. 并发-ThreadLocal源码分析

    ThreadLocal源码分析 参考: http://www.cnblogs.com/dolphin0520/p/3920407.html https://www.cnblogs.com/coshah ...

  9. Java -- 基于JDK1.8的ThreadLocal源码分析

    1,最近在做一个需求的时候需要对外部暴露一个值得应用  ,一般来说直接写个单例,将这个成员变量的值暴露出去就ok了,但是当时突然灵机一动(现在回想是个多余的想法),想到handle源码里面有使用过Th ...

随机推荐

  1. vue-element-admin跟springboot+shiro部署爬坑记

    今天把前端采用vue-element-admin与springboot的项目部署到正是线上,在开发线上很OK的,一放上去我的天啊,坑是真的多阿.下面听我一一道来:我这边采用的是nginx服务器部署. ...

  2. 微信小程序中concat 和push的区别

    push和concat二者功能很相像,但有两点区别. 先看如下例子: var arr = []; arr.push(1); arr.push(2); arr.push([3, 4]) arr.push ...

  3. Spring入门篇——第1章 概述

    第1章 概述 本章对课程的情况进行介绍,并介绍框架和Spring概况. 1-1 Spring入门课程简介 1-2 Spring概况 1-3 Spring框架

  4. node.js http模块和fs模块上机实验·

    httpserver const httpserver = require('http'); var server = httpserver.createServer(function (req,re ...

  5. python--ctypes模块:调用C函数

    Python 的 ctypes 要使用 C 函数,需要先将 C 编译成动态链接库的形式,即 Windows 下的 .dll 文件,或者 Linux 下的 .so 文件 Windows 系统下的 C 标 ...

  6. 题解 [51nod1201] 整数划分

    题面 解析 首先,因为是不同的数字, 可以从小到大依次枚举加上每一个数字的贡献,再枚举每个数. 然而这样会T掉... 考虑到\(n\)只有\(50000\), 当分成的数最多时,设最大的数为\(m\) ...

  7. composer查看全局配置

    composer config -l -g composer 更新慢 composer下载不下来问题解决 使用 Composer 镜像加速有两种选项: 选项一:全局配置,这样所有项目都能惠及(推荐): ...

  8. luogu4281

    P4281 [AHOI2008]紧急集合 / 聚会 题目描述 欢乐岛上有个非常好玩的游戏,叫做“紧急集合”.在岛上分散有N个等待点,有N-1条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道 ...

  9. java虚拟机JVM

    Java Virtual Machine ,简称JVM; 它是运行所有Java程序的抽象计算机,是Java语言的运行环境,它是Java 最具吸引力的特性之一,JVM读取并处理编译过的与平台无关的字节码 ...

  10. NSPredicate谓词的用法

    在IOS开发Cocoa框架中提供了一个功能强大的类NSPredicate,下面来讨论一下它的强大之处在哪...NSPredicate继承自NSObject,它有两个派生的子类• NSCompariso ...