ThreadLocal,很多人都叫它做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。

可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那样每个线程可以访问自己内部的副本变量。

这句话从表面上看起来理解正确,但实际上这种理解是不太正确的。下面我们细细道来。

多线程并发执行时,需要数据共享,因此才有了volatile变量解决 多线程间的数据可见性,

也有了锁的同步机制,使变量或代码块在某一时该,只能被一个线程访问,确保共享数据的正确性。(Synchronized用于线程间的数据共享的)

多线程并发执行时,并不是所有数据都需要共享的,这些不需要共享的数据,让每个线程去维护就OK了,ThreadLocal就是用于线程间的数据隔离的。

深入解析ThreadLocal类:

先我们来看一下ThreadLocal类是如何为每个线程创建一个变量的副本的。

  先看下get方法的实现:

  

第一句是取得当前线程,然后通过getMap(t)方法获取到一个map,map的类型为ThreadLocalMap。

然后接着下面获取到Entry键值对,注意这里获取Entry时参数传进去的是  this,即ThreadLocal实例,而不是当前线程t。如果获取成功,则返回value值。 

如果map为空,则调用setInitialValue方法返回value。

接着看一下getMap方法中做了什么:

  

在getMap中,是调用当期线程t,返回当前线程t中的一个成员变量threadLocals,类型为ThreadLocalMap。

这里意味着每一个线程都自带一个ThreadLocalMap成员变量。

继续取看ThreadLocalMap的实现:

 

可以看到ThreadLocalMap的Entry继承了WeakReference,并且使用ThreadLocal作为键值

也就是说WeakReference封装了ThreadLocal,并作为了ThreadLocalMap的Entry的Key。

总结一下,在每个线程Thread内部有一个ThreadLocalMap类型的成员变量threadLocals,

这个ThreadLocalMap成员变量的Entry的Key,当前ThreadLocal变量的WeakReference封装,value为变量。

为何ThreadLocalMap的键值为ThreadLocal对象? 因为每个线程中可能需要有多个threadLocal变量,也就是ThreadLocalMap里面可能会有多个Entry。

在每个线程内部 第一次调用ThreadLocal.get方法时,都会返回Null。因为默认情况下,initialValue方法返回的是null。

null 赋给(强转) 基本数据类型时会抛的空指针,null赋给 引用类型没问题。

可以在ThreadLocal的构造函数重写initialValue()方法。如下

ThreadLocal<Long> longLocal = new ThreadLocal<Long>(){

        protected Long initialValue() {
            return Thread.currentThread().getId();
        };
    };

或者在调用ThreadLocal.get方法之前,需要先执行set(),以保证threadlocals中有值。

或者value为引用类型变量null赋给 引用类型没问题。,如下,hibernate中典型的ThreadLocal的应用:

  1. private static final ThreadLocal threadSession = new ThreadLocal();
  2. public static Session getSession() throws InfrastructureException {
  3. Session s = (Session) threadSession.get();
  4. try {
  5. if (s == null) {
  6. s = getSessionFactory().openSession();  //分发一个实例 的内存地址,一般就是new一个实例了
  7. threadSession.set(s);
  8. }
  9. } catch (HibernateException ex) {
  10. throw new InfrastructureException(ex);
  11. }
  12. return s;
  13. }

开篇说ThreadLocal创建副本 的说法是不太正确的。为什么?

从上面这个hibernate的例子来看,这是一个使用ThreadLocal解决数据库连接的单例 在多线程中同时操作查询和关闭的情况。

首先这里面不是创建副本,而是分发新的内存地址(即,新的数据库连接的单例的内存地址,以当前ThreadLocal为key,value指向传入新的数据库连接的单例的内存地址。

从而达到单个线程获取数据连接的线程安全而已,也就是每个线程都有一个独立的数据库连接的单例

假设相反情况,一个数据库连接单例 如果在2个线程中被同时引用,2线程分别同一时间操作读取和close,肯定会出现冲突。

所以需要减少每次new的开销还是得使用数据库连接池

ThreadLocal的内存泄露问题:

当使用线程池来复用线程时,一个线程使用完后并不会销毁线程,那么 分发的那个实例会一直绑定在这个线程上。

由于WeakReference封装了ThreadLocal,并作为了ThreadLocalMap的Entry的Key。如果在某些时候ThreadLocal对象被赋Null的话,弱引用会被GC收集,这样就会导致Entry的Value对象找不到,

线程被复用后如果有调用ThreadLocal.get/set方法的话,方法里面会去做遍历清除  以[ThreadLocal=Null ]为Key的Entry; 但如果一直没调用ThreadLocal.get/set方法的话就会导致内存泄漏了。

所以一般线程用完ThreadLocal后,要调用threadLocal.remove(); 如下

  1. public static void close() {
  2. // 获取当前线程内共享的Connection
  3. Connection conn = threadLocal.get();
  4. try {
  5. // 判断是否已经关闭
  6. if(conn != null && !conn.isClosed()) {
  7. // 关闭资源
  8. conn.close();
  9.  // 移除Connection  
  10. threadLocal.remove();  
  11. conn = null;
  12. }
  13. } catch (SQLException e) {
  14. // 异常处理
  15. }
  16. }

深入理解java:2.4. 线程本地变量 java.lang.ThreadLocal类的更多相关文章

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

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

  2. linux TLS 线程本地变量

    最近在写底层hook的时候, 涉及到线程安全问题, 最开始我设计的时候使用的互斥量, 但是考虑到都是底层函数,加锁会导致性能问题, 一直在思考优化方案, 后来偶然想到,java里面有线程本地变量的AP ...

  3. .Net学习难点讨论系列17 - 线程本地变量的使用

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  4. .Net - 线程本地变量(存储)的使用

    关于C#多线程的文章,大部分都在讨论线程的开始与停止或者是多线程同步问题.多线程同步就是在不同线程中访问同一个变量或共享资源,众所周知在不使用线程同步的机制下,由于竞争的存在会使某些线程产生脏读或者是 ...

  5. Threadlocal线程本地变量理解

    转载:https://www.cnblogs.com/chengxiao/p/6152824.html 总结: 作用:ThreadLocal 线程本地变量,可用于分布式项目的日志追踪 用法:在切面中生 ...

  6. 通过transmittable-thread-local源码理解线程池线程本地变量传递的原理

    前提 最近一两个月花了很大的功夫做UCloud服务和中间件迁移到阿里云的工作,没什么空闲时间撸文.想起很早之前写过ThreadLocal的源码分析相关文章,里面提到了ThreadLocal存在一个不能 ...

  7. java线程本地变量

      ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量).也许把它命名为Thre ...

  8. Java并发机制(4)--ThreadLocal线程本地变量(转)

    个人理解: 说明:看了博客园中大神写的ThreadLocal的详解,感觉还是有些迷糊,下面用自己的理解简单描述下ThreadLocal的机制(难免有误): 1.首先ThreadLocal用于存储对应线 ...

  9. 线程本地变量ThreadLocal

    一.本地线程变量使用场景 并发应用的一个关键地方就是共享数据.如果你创建一个类对象,实现Runnable接口,然后多个Thread对象使用同样的Runnable对象,全部的线程都共享同样的属性.这意味 ...

随机推荐

  1. 2019ICPC沈阳网络赛-D-Fish eating fruit(树上DP, 换根, 点分治)

    链接: https://nanti.jisuanke.com/t/41403 题意: State Z is a underwater kingdom of the Atlantic Ocean. Th ...

  2. QT:QSS完全无效的原因

    QSS的文件格式不是UTF-8,导致读取到的文件中字符串出现乱码.

  3. ueditor+复制word+图片不能上传

    最近公司做项目需要实现一个功能,在网页富文本编辑器中实现粘贴Word图文的功能. 我们在网站中使用的Web编辑器比较多,都是根据用户需求来选择的.目前还没有固定哪一个编辑器 有时候用的是UEditor ...

  4. extern、static

    1. 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说exter ...

  5. TTTTTTTTTTTT CF 653D 送邮递员

    链接:给一张n个点m条带权边的有向图,有x个人从起点出发到终点,每个人带的都带相同重量的货物, 规定一条边最多能经过其上权的重量的货物,问最多能带多重的货物? 2 ≤ n ≤ 50, 1 ≤ m ≤  ...

  6. kmeans与kmeans++的python实现

    一.kmeans聚类: 基本方法流程 1.首先随机初始化k个中心点 2.将每个实例分配到与其最近的中心点,开成k个类 3.更新中心点,计算每个类的平均中心点 4.直到中心点不再变化或变化不大或达到迭代 ...

  7. 12.Python数值类型(整形、浮点型和复数)及其用法

    实际开发中,我们经常需要使用数字记录游戏中用户的得分.游戏中角色的生命值.伤害值等信息,Python 语言提供了数值类型用于保存这些数值. 需要注意的是,Python 中这些数值类型都是不可改变的,也 ...

  8. 大哥带的XSS练习LEVE3

    0X01DOM-XSS进阶之inner显式输出 首先我们先了解一下DOM型和和其他到底有什么区别 dom就是一个树状的模型,你可以编写Javascript代码根据dom一层一层的节点,去遍历/获取/修 ...

  9. C++入门经典-例5.14-丢失的内存,关于内存泄漏

    1:代码如下: // 5.14.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> usin ...

  10. snmpEngineBoots & snmpEngineID数据存储到非易失性存储设备

    #include <stdio.h> #include <stdlib.h> #include <string.h> int regenerateID() { ; ...