简介

  ThreadLocal在Java多线程开发中常见的一个类,在面试中也经见的问题,比如ThreadLocal的作用是什么,ThreadLocal的实现原理是什么等等。ThreadLocal是java中一个类,用于实现变量在多线程并发环境下维持线程的封闭性(封闭指的是可变对象对于其他线程是不可访问,不可操作的)。所以ThreadLocal其实可以当做一个线程内的局部变量来理解。

使用场景

当我们创建一个数据库连接,并且不希望在代码中互相传递,我们会将它设置成全局变量。然而数据库连接并不一定是线程安全的,我们希望每一个线程维持一个连接。

 private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
}; public static Connection getConnection() {
return connectionHolder.get();
}

  如上述代码,当不同的线程来调用getConnection方法的时候,获取到是不一样的Connection对象。hibernate的session管理也是通过ThreadLocal来实现的。

源码分析

  先来看一下ThreadLocal中最常用的几个方法。

     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 getMap(Thread t) {
return t.threadLocals;
} public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

  ThreadLocal的get方法和set方法算是最常用的方法了。第3行和第21中对ThreadLocal的操作都是通过一个ThreadLocalMap进行读写来完成get和set,ThreadLocalMap是一个散列表。从第5行和第23行看出,ThreadLocalMap的key是this,指代的是ThreadLocal本身,value则是线程对应的变量值。有一点要注意的是,ThreadlocalMap是在ThreadLocal内部定义的静态内部类,却是Thread类的属性。

  刚才说过ThreadlocalMap是一个散列表,但是它跟HashMap不一样。HashMap解决Hash冲突的方式是使用分离链表法,但是ThreadlocalMap是通过线性探测法来解决Hash冲突,这里就不对这方面进行细讲。

  

内存泄漏

  简介了ThreadLocal不顺便讲一讲ThreadLocal内存泄漏问题感觉很不专业,但是讲这个问题之前,可以先看一下上面图中,ThreadLocalMap的Entry使用的是一个弱引用(WeakReference),Java的引用分为强软弱虚四种,弱引用的特点是,当一个对象只被一个弱引用链接的时候,下次GC就会将刚对象回收。其他的引用细节这里不做赘述。

  内存泄漏原因:当一个ThreadLocal对象使用完毕被系统回收之后,因为Entry中的key为弱引用(这里并非整个entry),ThreadLocalMap中就有一个Entry中的Key会被设置为Null,此时value仍然保存和entry之间的强链接,导致value无法回收。假如线程一直不关闭,而ThreadLocalMap有大量的key被回收,就会存在一堆entry的key为null,value各不相同且无法回收的状况,所以ThreadLocal的get、set、remove中都会进行对key==null的entry进行清理。

  有人说内存泄漏是因为弱引用引起的,其实并不是,如果使用强引用,则会导致必须手动清除不需要使用的entry。使用上更加麻烦,并且更加容易出Bug。

  总的来说,当ThreadLocal变量已经不使用的时候,最好做一次remove()动作进行清理,这会是一个好习惯。

ThreadLocal简析的更多相关文章

  1. 一个错误使用单例模式的场景及ThreadLocal简析

    近来参与一个Java的web办公系统,碰到一个bug,开始猜测是线程池管理的问题,最后发现是单例模式的问题. 即,当同时发起两个事务请求时,当一个事务完成后,另一个事务会抛出session is cl ...

  2. 简析ThreadLocal原理及应用

    简析ThreadLocal原理及应用 原创: 东晨雨 JAVA万维猿圈 4月17日 ThreadLocal的源码加上注释不超过八百行,源码结构清晰,代码也比较简洁.ThreadLocal可以说是Jav ...

  3. JDK框架简析--java.lang包中的基础类库、基础数据类型

    题记 JDK.Java Development Kit. 我们必须先认识到,JDK不过,不过一套Java基础类库而已,是Sun公司开发的基础类库,仅此而已,JDK本身和我们自行书写总结的类库,从技术含 ...

  4. CGLib 简析

    背景 JDK 动态代理存在的一些问题: 调用效率低 JDK 通过反射实现动态代理调用,这意味着低下的调用效率: 每次调用 Method.invoke() 都会检查方法的可见性.校验参数是否匹配,过程涉 ...

  5. 简析.NET Core 以及与 .NET Framework的关系

    简析.NET Core 以及与 .NET Framework的关系 一 .NET 的 Framework 们 二 .NET Core的到来 1. Runtime 2. Unified BCL 3. W ...

  6. 简析 .NET Core 构成体系

    简析 .NET Core 构成体系 Roslyn 编译器 RyuJIT 编译器 CoreCLR & CoreRT CoreFX(.NET Core Libraries) .NET Core 代 ...

  7. RecycleView + CardView 控件简析

    今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...

  8. Java Android 注解(Annotation) 及几个常用开源项目注解原理简析

    不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...

  9. PHP的错误报错级别设置原理简析

    原理简析 摘录php.ini文件的默认配置(php5.4): ; Common Values: ; E_ALL (Show all errors, warnings and notices inclu ...

随机推荐

  1. jquery实现视频滚动

    原理图 <!DOCTYPE html> <html lang="en"> <head> <script src="jquery. ...

  2. 20-Ubuntu-文件和目录命令-查看目录树型结构-tree

    tree 以树状图列出当前目录下的文件目录结构 选项 含义 -d 只显示当前目录的子目录树型结构   显示当前目录的子目录和文件树型结构 例: 1.查看文档目录下的子目录和文件树型结构 2.查看文档目 ...

  3. 【牛客提高训练营5B】旅游

    题目 吉老师的题时过一年还是不会做 从\(1\)号点出发经过每条边至少一次并且还要回到\(1\)号点,这跟欧拉回路的条件非常像,但是欧拉回路的实际上是"经过每一条边恰好一次并且回到出发点&q ...

  4. LA2218 Triathlon /// 半平面交 oj22648

    题目大意: 铁人三项分连续三段:游泳 自行车 赛跑 已知各选手在每个单项中的速度v[i],u[i],w[i] 设计每个单项的长度 可以让某个特定的选手获胜 判断哪些选手有可能获得冠军 输出n行 有可能 ...

  5. Immutable 想破坏它也没办法

    上一章讲的是线程互斥的synchronized实现,这样做会影响性能,如何才能做到既不影响性能又能达到线程安全的目的呢,就是使用状态绝不会改变的类,Java中的应用就是String类. public ...

  6. 关于pip安装较慢的问题解决

    在 ~/.pip/ 下创建文件 pip.conf(如果还没有的话),并填入以下内容: [global] timeout = 6000 index-url = http://pypi.douban.co ...

  7. Git 获取项目git clone

    git clone 克隆项目 git clone 实际上是一个封装了其他几个命令的命令. 它创建了一个新目录,切换到新的目录,然后 git init 来初始化一个空的 Git 仓库, 然后为你指定的 ...

  8. MySQL 其他基础知识

    -- 查询存储引擎show engines;-- 显示可用存储引擎show variables like 'have%'; -- concat多个字段联合select tname ,cname ,co ...

  9. Kunbernetes从私有仓库nexus拉取镜像

    1.docker登陆认证 [root@master ~]# vim /etc/docker/daemon.json { "insecure-registries": [" ...

  10. LUOGU P4042 [AHOI2014/JSOI2014]骑士游戏 (spfa+dp)

    传送门 解题思路 首先设\(f[x]\)表示消灭\(x\)的最小花费,那么转移方程就是 \(f[x]=min(f[x],\sum f[son[x]] +s[x])\),如果这个转移是一个有向无环图,那 ...