在一般的网站开发中,基于Java的Web 框架都使用了ThreadLocal来存储一些全局的参数,在拦截器\Filter中设置变量,让变量可以在任意地方被获取。

一早就了解到里面有用到WeakReference(弱引用),但对弱引用仅限于一种懵懂的概念,并且认为只要GC,弱引用的对象就被回收掉了,实际情况呢?

Thread对象有一个变量名为 threadLocals 的 ThreadLocalMap对象,这个类和HashMap类似,里面定义了一个Entry数组,不过这个Entry对象弱引用了ThreadLocal

/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value; Entry(ThreadLocal k, Object v) {
super(k); //调用父类构造函数,设置Reference的referent属性
value = v;
}
}
/**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal key, Object value) { // We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not. Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal k = e.get(); 获取Reference的referent属性 if (k == key) {
e.value = value;
return;
} if (k == null) {
replaceStaleEntry(key, value, i); //清理掉陈旧Entry,这种Entry是由于ThreadLocal变量对象被回收后k==null造成的
return;
}
} tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}

注意以下方法

                if (k == null) {
replaceStaleEntry(key, value, i); //清理掉陈旧Entry,这种Entry是由于ThreadLocal变量对象被回收后k==null造成的
return;
}

这个地方其实也解释了通常所说的ThreadLocal变量可能导致内存溢出的问题:

Entry弱引用了ThreadLocal,因此Entry是否存活,不会影响ThreadLocal的生命周期,ThreadLocal在没有其他对象引用后被回收,但是它对应的Entry中的value实际还在被Entry引用,而这不是一个弱引用,如果不清掉掉Entry,value就被一直强引用,无法释放,那么就可能会内存溢出

如果一直没有ThreadLocal变量访问,并且线程一直存活,就不会清理陈旧Entry,value永远无法释放,这也是某种意义上的内存泄露

一直以来,我把WeakReference理解成:弱引用的对象一定会在下次GC时回收掉,按此推断,ThreadLocal变量是不太安全的,因为用着用着就可能被GC掉了。

但是如果ThreadLocal有这么严重的问题,谁会去用呢!

实际上对象B弱引用了A,如果A除了B以外,没有其他引用(强、软引用)时,才会把A GC调,这也是为什么ThreadLocal实际上是一个安全的操作。

Thread.threadLocals 是一个Map,每个Entry都弱引用了ThreadLocal对象
因此Thread.threadLocals对每个ThreadLocal对象都是弱引用关系

调用ThreadLocal.get()方法

/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); 可以看到get实际上是获取当前线程的ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
} /**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

可以理解为一个ThreadLocal实例,在多个线程都存在副本,并且在不同线程设置的值,都不会影响到其他线程,因为ThreadLocal实例是存储在当前Thread对象上的

也就是说,如果Thread死掉后,它和ThreadLocal对象也就没有任何关系了,这对我们的程序没有任何影响,因为我们也没有机会再取它在Thread中的副本了;

而ThreadLocal变量被设置为null后,虽然Thread中的Entry弱引用了ThreadLocal,即使Thread对象还在,当ThreadLocal对象没有被其他对象引用后,它就可以被GC调了,Thread对象产生了弱引用,不会影响到ThreadLocal变量的回收

其他知识点:

需要注意的是,当一个线程执行Thread.start()后,如果start()方法已经执行完毕了,虽然Thread对象还在,其实它的生命周期已经结束了,对应的ThreadLocal变量可以被回收

如果线程再次调用Thread.start()方法,会抛出异常,因此不用关注线程再次被启动,因为它已经无法再被启动了(PS:线程复用技术并非是重新start,而是在线程内执行多个任务)

以下情况ThreadLocal变量是永远不会回收的

public class FlagContext {

    /**
* ThreadLocal变量
*/
private final static ThreadLocal<Boolean> tbFlag = new ThreadLocal<Boolean>();
}

FlagContext的静态变量引用了ThreadLocal实例tbFlag,tbFlag是个final 声明的变量,永远不会为空,因此,这种情况下,tbFlag对应的值,只会在线程生命周期结束,或调用tbFlag.remove()才会被回收!原因呢:

ThreadLocal永远被FlagContext强引用了,而Thread和ThreadLocal不管是否是弱引用,即使Thread中的Entry被回收了,也不会被GC掉!

ThreadLocal中的WeakReference的更多相关文章

  1. ThreadLocal中优雅的数据结构如何体现农夫山泉的广告语

    本篇文章主要讲解 ThreadLocal 的用法和内部的数据结构及实现.有时候我们写代码的时候,不太注重类之间的职责划分,经常造出一些上帝类,也就是什么功能都往这个类里放.虽然能实现功能但是并不优雅且 ...

  2. juce 中的WeakReference分析

    juce中的WeakReference设计得比较巧妙,巧妙就是使用delete之后就可以通知道WeakReference,原理其实也很间单,其实就是在对象里添加了一个子对象masterReferenc ...

  3. 正确理解ThreadLocal:ThreadLocal中的值并不一定是完全隔离的

    首先再讨论题主的这个观点之前我们要明确一下ThreadLocal的用途是什么? ThreadLocal并不是用来解决共享对象的多线程访问问题. 看了许多有关ThreadLocal的博客,看完之后会给人 ...

  4. Java中关于WeakReference和WeakHashMap的理解

    新美大的10月11日的笔试中有一道选择题,让选择函数返回结果,代码如下: private static String test(){ String a = new String("a&quo ...

  5. Java中的 WeakReference 和 SoftReference

    我们知道Java语言中没有指针,取而代之的是引用reference.Java中的引用又可以分为四种:强引用,弱引用(WeakReference),软引用(SoftReference),虚引用(Phan ...

  6. 谈谈java中的WeakReference

    Java语言中为对象的引用分为了四个级别,分别为 强引用 .软引用.弱引用.虚引用. 本文只针对java中的弱引用进行一些分析,如有出入还请多指正. 在分析弱引用之前,先阐述一个概念:什么是对象可到达 ...

  7. Android中的WeakReference 弱引用

    WeakReference 弱引用 定义:弱引用,与强引用(我们常见的引用方式)相对:特点是:GC在回收时会忽略掉弱引用对象(忽略掉这种引用关系),即:就算弱引用指向了某个对象,但只要该对象没有被强引 ...

  8. 【转】Java中关于WeakReference和WeakHashMap的理解

    新美大的10月11日的笔试中有一道选择题,让选择函数返回结果,代码如下: private static String test(){ String a = new String("a&quo ...

  9. 理解java中的ThreadLocal(转)

    一.对ThreadLocal概述 JDK API 写道: 该类提供了线程局部 (thread-local) 变量.这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的 ...

随机推荐

  1. 原生js+css实现重力模拟弹跳系统的登录页面

    今天小颖把之前保存的js特效视频看了一遍,跟着视频敲了敲嘻嘻,用原生js实现一个炫酷的登录页面.怎么个炫酷法呢,看看下面的图片大家就知道啦. 效果图: 不过在看代码之前呢,大家先和小颖看看css中的o ...

  2. 如何阻止sql注入(pdo篇)

    Use prepared statements and parameterized queries. These are SQL statements that are sent to and par ...

  3. github和本地仓库关联

    1.安装git 2.在github上注册账号 3.github上创建repository 4.克隆github上的repository 5.在target directory中右键打开git gui

  4. Leetcode题解(31)

    103. Binary Tree Zigzag Level Order Traversal 题目 分析: 广度优先遍历的应用.重点是掌握vector的reverse函数,一开始我忘记有这个函数了,琢磨 ...

  5. 最长公共子序列Lcs(打印路径)

    给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的).   比如两个串为:   abcicba abdkscab   ab是两个串的子序列,abc也是,abca也是,其中abca是这 ...

  6. Problem G

    Problem Description A relay is a race for two or more teams of runners. Each member of a team runs o ...

  7. Game

    Game Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submissi ...

  8. AngularJS学习篇(十)

    AngularJS Select(选择框) 使用 ng-options 创建选择框 在 AngularJS 中我们可以使用 ng-option 指令来创建一个下拉列表,列表项通过对象和数组循环输出,如 ...

  9. Problem F: 分数类的类型转换

    Description 封装一个分数类Fract,用来处理分数功能和运算,支持以下操作:   1. 构造:传入两个参数n和m,表示n/m:分数在构造时立即转化成最简分数. 2. show()函数:分数 ...

  10. vue-cli 前端开发,后台接口跨域代理调试问题

    使用 webpack的方式开发的时候,前台开发过程中需要调用很多后台的数据接口,但是通常前后台分离的开发方式,后台的接口数据很可能是不方便或者是不能在前端同学的电脑上运行的,也就出现了所谓的跨域问题. ...