回顾ThreadLocal
ThreadLocal作为解决特定场景下并发的一种方案,在Spring等框架及面试中经常会被问到,它是Java必须要掌握的基础知识之一。
ThreadLocal类的作用是抽象线程内变量的抽象,这类对象只在线程生命周期内起作用,不会被其它线程访问修改,它可以减少线程内多个函数或组件之间传递信息的复杂度,并且这类变量不存在多线程并发访问问题从而时间性能更好。
一、ThreadLocal的实现原理
首先在线程抽象类Thread中有一个ThreadLocal.ThreadLocalMap类型的threadLocals引用,它指向的ThreadLocalMap类中有一个Entry[]数组,其中每个Entry对象key为ThreadLocal的实例对象,value值为ThreadLocal对象设置的值。其主要源码如下:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
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;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocal变量是在代码中定义,在代码执行时线程对象拿到自己ThreadLocalMap类型的成员变量的值(可看做一个Map),然后将ThreadLocal对象做为Key和ThreadLocal对象设置的对象值作为value组成的Entry对象加入进来。
由于ThreadLocal对象作为可以,所以必须要区分每个ThreadLocal的实例对象,为此ThreadLocal类定义中定义了如下成员:
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode = new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
通过final int型的threadLocalHashCode成员区分不同的ThreadLocal对象,它在构造时通过nextHashCode()函数复制,而nextHashCode()又通过原子变量自增某个固定值来保证线程安全。
注意ThreadLocalMap并没有实现Map接口,它内部是一个Entry数组,里面每个Entry对象保存一个key-value键值对,key是ThreadLocal对象,value是ThreadLocal对象set方法设置的对象值。即通过ThreadLocal的set方法把ThreadLocal对象自己当做key,参数值当做value,放进了ThreadLocalMap中。ThreadLocalMap的Entry与HashMap的Entry相比没有next字段,因此不存在链表的情况,出现hash冲突时就将元素放到数组中下一个位置,其插入一个key-value的实现如下:
private void set(ThreadLocal<?> key, Object value) {
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();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
二、ThreadLocal可能引起的内存泄漏
ThreadLocal对象间的引用关系图如下,虚线表示弱引用,如果ThreadLocal对象没有外部强引用指向它,在系统gc时就会被回收,此时ThreadLocalMap中就会出现key为null的Entry,而程序中也无法访问这些key为null的Entry,但如果当前线程不结束的话(尤其是在线程池线程复用一直不结束的场景下),这个key为null的Entry的value就会一直存在一个从GcRoots过来的强引用链:Thread Ref——》Thread——》ThreadLocalMap——》Entry——》value,无法被内存回收,因此造成内存泄漏,即在threadLocal设为null和线程结束前这段时间内Entry不会被回收,造成了内存泄漏。

解决方法,ThreadLocal对象在使用完后调用remove方法删除它。
JDK也建议将ThreadLocal变量定义为private static的,这样ThreadLocal对象的生命周期就长(一直存在ThreadLocal对象的强引用,所以它不会被回收),从而能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,在用完后调用remove方法删除它。
https://www.cnblogs.com/xzwblog/p/7227509.html
回顾ThreadLocal的更多相关文章
- ThreadLocal内存泄漏真因探究(转)
出处: 链接:https://www.jianshu.com/p/a1cd61fa22da ThreadLocal原理回顾 ThreadLocal的原理:每个Thread内部维护着一个ThreadLo ...
- Java:ThreadLocal小记
Java:ThreadLocal小记 说明:这是看了 bilibili 上 黑马程序员 的课程 java基础教程由浅入深全面解析threadlocal 后做的笔记 内容 ThreadLocal 介绍 ...
- ThreadLocal的介绍与运用
ThreadLocal全面解析 学习目标 了解ThreadLocal的介绍 掌握ThreadLocal的运用场景 了解ThreadLocal的内部结构 了解ThreadLocal的核心方法源码 了解T ...
- 【Java EE 学习 54】【OA项目第一天】【SSH事务管理不能回滚问题解决】【struts2流程回顾】
一.SSH整合之后事务问题和总结 1.引入问题:DAO层测试 假设将User对象设置为懒加载模式,在dao层使用load方法. 注意,注释不要放开. 使用如下的代码块进行测试: 会报错:no sess ...
- 深入理解ThreadLocal(转)(2015年06月11日)
注明:转自:http://my.oschina.net/clopopo/blog/149368 学习一个东西首先要知道为什么要引入它,就是我们能用它来干什么.所以我们先来看看ThreadLocal对我 ...
- 线程知识-ThreadLocal使用详解
最近在看Spring的时候回顾了一下ThreadLocal,下面是ThreadLocal的使用说明. 概述 首先,谈到ThreadLocal的使用,我们先来了解一下ThreadLocal是什么?Thr ...
- 计算机程序的思维逻辑 (82) - 理解ThreadLocal
本节,我们来探讨一个特殊的概念,线程本地变量,在Java中的实现是类ThreadLocal,它是什么?有什么用?实现原理是什么?让我们接下来逐步探讨. 基本概念和用法 线程本地变量是说,每个线程都有同 ...
- 并发编程(四):ThreadLocal从源码分析总结到内存泄漏
一.目录 1.ThreadLocal是什么?有什么用? 2.ThreadLocal源码简要总结? 3.ThreadLocal为什么会导致内存泄漏? 二.ThreadLoc ...
- ThreadLocal源码解读
1. 背景 ThreadLocal源码解读,网上面早已经泛滥了,大多比较浅,甚至有的连基本原理都说的很有问题,包括百度搜索出来的第一篇高访问量博文,说ThreadLocal内部有个map,键为线程对象 ...
随机推荐
- python中使用configparser库,实现配置文件的读取
背景: 在写接口自动化框架,配置数据库连接时,测试环境和UAT环境的连接信息不一致,这时可以将连接信息写到conf或者cfg配置文件中 python环境请自行准备. python代码直接封装成类,方便 ...
- 第 8 章 容器网络 - 069 - Calico 的默认连通性
相同calico 网络之间的连通性 测试一下 bbox1 与 bbox2 的连通性: ping 成功,数据包流向如下图所示. 1)根据 bbox1 的路由表,将数据包从 cal0 发出. 2)数据经过 ...
- php格式化json字符串
header('content-type:application/json;charset=utf8'); $arr = array( 'status' => true, 'errMsg' =& ...
- guxh的python笔记七:抽象基类
1,鸭子类型和白鹅类型 1.1,白鹅类型 白鹅类型对接口有明确定义,比如不可变序列(Sequence),需要实现__contains__,__iter__,__len__,__getitem__,__ ...
- linux文件管理之链接文件
文件链接 ====================================================================================软链接 或 符号链接硬 ...
- redis的发布订阅、持久化存储、redis的主从复制
redis的发布订阅 1. 创建redis配置文件 vim /opt/redis_conf/reids-6379.conf mkdir /data/6379 redis-server redis-6 ...
- Python — 字典dict 和 集合set
字典dict : dict和set的key都是不可变对象 对于不变对象来说,调用对象自身的任意方法,也不会改变对象自身的内容.相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可 ...
- 『TensorFlow』单&双隐藏层自编码器设计
计算图设计 很简单的实践, 多了个隐藏层 没有上节的高斯噪声 网络写法由上节的面向对象改为了函数式编程, 其他没有特别需要注意的,实现如下: import numpy as np import mat ...
- QComboBox样式
https://blog.csdn.net/lys211/article/details/43956979https://blog.csdn.net/x_nazgul/article/details/ ...
- C++解析九-数据抽象
数据抽象 数据抽象是指,只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节.数据抽象是一种依赖于接口和实现分离的编程(设计)技术.让我们举一个现实生活中的真实例子,比如一台电 ...