回顾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,键为线程对象 ...
随机推荐
- .net中文分词 jieba.NET
简介 平时经常用Python写些小程序.在做文本分析相关的事情时免不了进行中文分词,于是就遇到了用Python实现的结巴中文分词.jieba使用起来非常简单,同时分词的结果也令人印象深刻,有兴趣的可以 ...
- Django 修改视图文件(views.py)并加载Django模块 + 利用render_to_response()简化加载模块 +locals()
修改视图代码,让它使用 Django 模板加载功能而不是对模板路径硬编码.返回 current_datetime 视图,进行如下修改: from django.template.loader impo ...
- fcn+caffe+voc2012实验记录
参考博客: http://blog.csdn.net/haoji007/article/details/77148374 http://blog.csdn.net/jacke121/article/d ...
- Http头:Expires,Cache-Control,Last-Modified,ETag
Expires:过期时间 el:Expirse:Fri,30 Oct 1998 14:19:41 Cache-Control:缓存控制 el:Cache-Contro ...
- 『TensorFlow』读书笔记_进阶卷积神经网络_分类cifar10_下
数据读取部分实现 文中采用了tensorflow的从文件直接读取数据的方式,逻辑流程如下, 实现如下, # Author : Hellcat # Time : 2017/12/9 import os ...
- android SDK SDK Manager.exe 无法打开,一闪而过最终解决办法
这个问题是由于Android Studio 自动更新之后 ,针对android-sdk-windows\tools 这个文件进行了拆分删除 这里做一下说明: 经过Android studio处理之后, ...
- ORA错误总结
ORA-12560 协议适配器错误 可能是以下原因: 1:服务没有开启(oracle的服务,oraclehome92TNSlistener) 2:数据库实例没有开启(oracleserviceORCL ...
- Matlab:高阶常微分三种边界条件的特殊解法(中心差分法,高精度导数边界处理)
函数文件1: function b=F(f,x0,h,N) % b(1,1)=x0(1)-h*x0(2)-u(1); % b(2,1)=x0(2)+h*x0(1)^2-u(2)-h*f; b=zero ...
- [hdu P4081] Qin Shi Huang’s National Road System
[hdu P4081] Qin Shi Huang’s National Road System Time Limit: 2000/1000 MS (Java/Others) Memory Li ...
- [poj P1475] Pushing Boxes
[poj P1475] Pushing Boxes Time Limit: 2000MS Memory Limit: 131072K Special Judge Description Ima ...