ThreadLocal中的WeakReference
在一般的网站开发中,基于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的更多相关文章
- ThreadLocal中优雅的数据结构如何体现农夫山泉的广告语
本篇文章主要讲解 ThreadLocal 的用法和内部的数据结构及实现.有时候我们写代码的时候,不太注重类之间的职责划分,经常造出一些上帝类,也就是什么功能都往这个类里放.虽然能实现功能但是并不优雅且 ...
- juce 中的WeakReference分析
juce中的WeakReference设计得比较巧妙,巧妙就是使用delete之后就可以通知道WeakReference,原理其实也很间单,其实就是在对象里添加了一个子对象masterReferenc ...
- 正确理解ThreadLocal:ThreadLocal中的值并不一定是完全隔离的
首先再讨论题主的这个观点之前我们要明确一下ThreadLocal的用途是什么? ThreadLocal并不是用来解决共享对象的多线程访问问题. 看了许多有关ThreadLocal的博客,看完之后会给人 ...
- Java中关于WeakReference和WeakHashMap的理解
新美大的10月11日的笔试中有一道选择题,让选择函数返回结果,代码如下: private static String test(){ String a = new String("a&quo ...
- Java中的 WeakReference 和 SoftReference
我们知道Java语言中没有指针,取而代之的是引用reference.Java中的引用又可以分为四种:强引用,弱引用(WeakReference),软引用(SoftReference),虚引用(Phan ...
- 谈谈java中的WeakReference
Java语言中为对象的引用分为了四个级别,分别为 强引用 .软引用.弱引用.虚引用. 本文只针对java中的弱引用进行一些分析,如有出入还请多指正. 在分析弱引用之前,先阐述一个概念:什么是对象可到达 ...
- Android中的WeakReference 弱引用
WeakReference 弱引用 定义:弱引用,与强引用(我们常见的引用方式)相对:特点是:GC在回收时会忽略掉弱引用对象(忽略掉这种引用关系),即:就算弱引用指向了某个对象,但只要该对象没有被强引 ...
- 【转】Java中关于WeakReference和WeakHashMap的理解
新美大的10月11日的笔试中有一道选择题,让选择函数返回结果,代码如下: private static String test(){ String a = new String("a&quo ...
- 理解java中的ThreadLocal(转)
一.对ThreadLocal概述 JDK API 写道: 该类提供了线程局部 (thread-local) 变量.这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的 ...
随机推荐
- DNS主从服务部署
(1)节点信息 console01 主DNS 192.168.80.3 192.168.10.3 console02 从DNS 192.168.80.4 192.168.10.4 (2)环境部署 # ...
- Windows环境下多线程编程原理与应用读书笔记(6)————临界段及其应用
<一>临界段 临界段对象通过提供所有线程必须共享的对象来控制线程.只有拥有临界段对象的线程才能够访问保护的资源.在另一个线程可以访问该资源之前,前一线程必须释放临界段对象,一遍新的线程可以 ...
- jumpserver安装详解
环境说明 主机为最小 安装的centos6.9 x86_64. [root@m01 ~]# cat /etc/redhat-release CentOS release 6.9 (Final) [ro ...
- struts2常见错误
1.Caught exception while loading file struts-default.xml - [unknown location]解决办法:由于lib包冲突造成的,将两个解析x ...
- D3.js从入门到“放弃”指南
前言 近期略有点诸事不顺,趁略有闲余之时,玩起D3.js.之前实际项目中主要是用各种chart如hightchart.echarts等,这些图形库玩起来貌都是完美的,一切皆可配置,但几年前接触了D3之 ...
- ①bootstrap引入
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Adobe Audio 分轨录音教程(需要KX,Live机架)
一.需要的硬件和软件 1. 创新5.1声卡或7.1声卡: 2. 已安装KX驱动和Live机架,经过测试安装后需要重启电脑才能生效. 3. 已安装Adobe Audition 3.0 二.测试环境 WI ...
- dubbo专题」dubbo其实很简单,就是一个远程服务调用的框架(1)
一.dubbo是什么? 1)本质:一个Jar包,一个分布式框架,,一个远程服务调用的分布式框架. 既然是新手教学,肯定很多同学不明白什么是分布式和远程服务调用,为什么要分布式,为什么要远程调用.我简单 ...
- js规范
js规范 Array 和 Object 直接量 为了避免这些歧义, 我们应该使用更易读的直接量来声明. var a = [x1, x2, x3]; var a2 = [x1, x2]; var a3 ...
- linux系统下手动安装Angular-cli
安装Angular-cli 背景 由于公司linux服务器没有外网,无法通过npm包管理器直接安装,只能手动安装一个Angular-cli平台环境! 安装步骤 1. 先再linux系统下安装好node ...