我是站长,从2003年开始接触计算机,至今已有16年。某重点大学硕士毕业。精通Java,C/C++,Python,熟悉Web开发,擅长大数据,对数学和英语情有独钟。本文是出自我的小册子:《一针见血ThreadLocal》。

前言:

最近发了一篇文章,关于threadlocal内存泄漏的,收藏量挺大的,不过我个人感觉还有些地方可以阐述地更一针见血,所以又重新修订了一版,欢迎大家收藏。

本文的阅读方法

通常情况下,人们对threadlocal的理解思路是这样的:首先把它放在线程这个大的背景下去琢磨,然后认为它是解决多线程的变量冲突问题。

但是按照这样的思路去学习,其实效果不好。由于"线程"这个东西是抽象的东西,对于所有的人来说,"线程"就是一只拦路虎,让每一个接触ThreadLocal的人都产生了内心的抵触,产生了虚无缥缈的无助感,所以我们对ThreadLocal的理解没有深度,没有灵性,很教条,很空洞。

何不抛开线程而从变量的角度来认识和掌握 ThreadLocal呢?毕竟threadlocal是由两个维度组成:线程和变量。所以,阅读本文之前先抛开线程这个东西,单单从变量这个角度切入。

以变量角度切入threadlocal,等到了一定程度再顺手串联thread,所以本文的整体思路是:变量->线程。而不是:线程->变量。

1、threadlocal的变量属性

抛开线程的属性,threadlocal就是一种类型的变量而已。对于普通的变量而言,我们很熟悉,其操作过程无非就是这样的:

int a; //定义变量,开辟一块内存
a = 10; //给变量赋值

如上代码所示,整数字面量10存储到了变量a里面。a表示一块内存。当a不再被使用的时候,会被Java虚拟机所回收。

对于同样是变量的threadlocal而言,它的用法是这样的:

//声明一个threadlocal变量b,此时开辟了一块内存,里面存放的b对象
ThreadLocal<Integer> b = new ThreadLocal<Integer>();

//注意,这里不是赋值,赋值是=操作符
b.set(10)

在上面这个代码里面,10是放在了b里面吗?这一点令人非常的困惑!一定要记住牢记下面两点:

(1)10不是放在了b里面,10和b是两个独立存放的东西,不是包含关系。

(2)10和b是两个独立存放的变量,如果其中的一个被清理,那么另外一个不受影响的。

既然10和b是独立存放的,那么它们之间到底有什么关系呢?其实,这种关系,可以通过一个小故事来阐述清楚:

在抗日时期,有两名地下党A和B,A是上线,B是下线,B不能直接联系党中央的,他需要通过A来帮忙传话。一旦A发生意外,党中央就找不到B了,B一直存在,但是茫茫人海,党中央是无法启用B做战斗任务的安排,这种情况类似内存泄漏。

2、存放在哪里?

如上代码所示,10和b存放在哪里呢?线程活着,都有一个map,类似于context,每个线程都有一个map,随时随地可以存放东西。既然是map,肯定是用key-value的形式存在,key就是b,value就是10。

3、薄命郎:弱引用

10和b两个的独立存放的东西,只不过我们不能直接访问到10,必须通过b来传话,原因很简单,在map里面,value要通过key来访问。

不过,此时b被包装成了弱引用,也就是说它被打了一个标签,这样它很容易被gc。一旦b被清理了,10就找不到了,从而造成了内存泄漏。

总之,b是个薄命郎,用他来做上线,他很容易挂掉的。上线挂了,下线自然就联系不上了。

4、小结

(1)有两个变量a和b,如果后面不再参与计算,则会被自动回收;

(2)有两个变量a和b,存放在map里面,a是key,b是value。如果map一直存在,a和b因为被map关联,则a和b就一直不能被回收;

备注:线程活着,都有map,类似于context,每个线程都有一个map,好比一个context一样,随时随地可以存放东西

(3)有两个变量a和b,存放在map里面,a是key(但是a被弱引用包装了一下),b是value。如果map一直存在,a和b因为被map关联,则b就一直不能被回收,但是a可以被回收。一旦a回收了,那么无法通过a找到b了,这就是b出现内存泄漏。

5、公众号

如果诸位从这篇文章读到了收获,恭喜您!欢迎关注网站的公众号:

标签: none

添加新评论