Threadlocal源码分析以及其中WeakReference作用分析
今天在看Spring 3.x企业应用开发实战,第九章 Spring的事务管理,9.2.2节ThreadLocal的接口方法时,书上有提到Threadlocal的简单实现,我就去看了下JDK1.8的Threadlocal的源码。发现实现方式与书中讲的并不相同,同时在网上搜索了一下,发现有比较多的人理解错了。
先看一下容易误导的解释:在ThreadLocal类中有一个Map对象,这个Map以每个Thread对象为键,保存了这个线程对应局部变量值,对应的实现方式如下:
public class SimpleThreadLocal {
private Map valueMap = Collections.synchronizedMap(new HashMap());
public void set(Object newValue) {
valueMap.put(Thread.currentThread(), newValue);//①键为线程对象,值为本线程的变量副本
}
public Object get() {
Thread currentThread = Thread.currentThread();
Object o = valueMap.get(currentThread);//②返回本线程对应的变量
if (o == null && !valueMap.containsKey(currentThread)) {//③如果在Map中不存在,放到Map 中保存起来。
o = initialValue();
valueMap.put(currentThread, o);
}
return o;
}
public void remove() {
valueMap.remove(Thread.currentThread());
}
public Object initialValue() {
return null;
}
}
为什么不按照那种有误的方法实现呢?
看起来似乎是不同线程获取了各自的值,但是这些值并没有线程独立。线程A可以操作线程B对应的值。如果某个线程将保存这些值的Map置为null了,那么其他线程也无法访问了。
实际上是怎样的呢
我们看ThreadLocal的get()方法源码。可以看到获取Threadlocal中的值,是先通过当前线程的线程对象t,获取t的ThreadlocalMap属性对象,然后再以Threadlocal对象为键,去获取ThreadlocalMap中的值。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//获取线程对象的属性
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//这里的this是指Threadlocal对象,Threadlocal在类中通常以静态属性出现,所以多个线程的Threadlocal指向同一个对象。
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
同时我们查看ThreadLocal源码中定义的静态类ThreadLocalMap,其实底层封装的是一个Entry数组,获取方式和普通的HashMap不太一样,如果没有命中,就直接通过线性搜索,因为ThreadLocalMap需要保存的Entry并不会太多。
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
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();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
通过ThreadLocal,每个线程保存自身的数据,不能访问到其他线程的数据。
ThreadLocalMap的Entry使用ThreadLocal的WeakReference引用作为Key值,当所有线程运行出ThreadLocal的作用域时,即没有强引用ThreadLocal时,ThreadLocal就会被回收。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
不是Threadlocal为每个线程提供了独立的变量,而是每个线程自己带了自己独立的变量。
关于内存泄漏
关于ThreadLocalMap的内存泄漏:如果一个ThreadLocal的生命周期结束,即在ThreadLocal所处的类中没有了强引用,而Thread没有结束,在Thread的threadLocals成员变量中,会有一个Entry使用弱引用引用了ThreadLocal作为key,因为是弱引用,这个key将被回收。而value是强引用,看起来是会造成泄漏,但是在ThreadLocalMap的set和get方法中,有一些释放的方法。具体的我也不太懂。
还是老老实实使用ThreadLocal的remove方法比较好。
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
Threadlocal源码分析以及其中WeakReference作用分析的更多相关文章
- Java并发编程之ThreadLocal源码分析
## 1 一句话概括ThreadLocal<font face="微软雅黑" size=4> 什么是ThreadLocal?顾名思义:线程本地变量,它为每个使用该对象 ...
- 并发编程(四)—— ThreadLocal源码分析及内存泄露预防
今天我们一起探讨下ThreadLocal的实现原理和源码分析.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用需要注意的地方,最后给出了两 ...
- ThreadLocal源码及相关问题分析
前言 在高并发的环境下,当我们使用一个公共的变量时如果不加锁会出现并发问题,例如SimpleDateFormat,但是加锁的话会影响性能,对于这种情况我们可以使用ThreadLocal.ThreadL ...
- ThreadLocal源码分析-黄金分割数的使用
前提 最近接触到的一个项目要兼容新老系统,最终采用了ThreadLocal(实际上用的是InheritableThreadLocal)用于在子线程获取父线程中共享的变量.问题是解决了,但是后来发现对T ...
- Java多线程学习之ThreadLocal源码分析
0.概述 ThreadLocal,即线程本地变量,是一个以ThreadLocal对象为键.任意对象为值的存储结构.它可以将变量绑定到特定的线程上,使每个线程都拥有改变量的一个拷贝,各线程相同变量间互不 ...
- ThreadLocal详解,ThreadLocal源码分析,ThreadLocal图解
本文脉路: 概念阐释 ----> 原理图解 ------> 源码分析 ------> 思路整理 ----> 其他补充. 一.概念阐述. ThreadLocal 是一个为 ...
- 【JAVA】ThreadLocal源码分析
ThreadLocal内部是用一张哈希表来存储: static class ThreadLocalMap { static class Entry extends WeakReference<T ...
- 并发-ThreadLocal源码分析
ThreadLocal源码分析 参考: http://www.cnblogs.com/dolphin0520/p/3920407.html https://www.cnblogs.com/coshah ...
- Java -- 基于JDK1.8的ThreadLocal源码分析
1,最近在做一个需求的时候需要对外部暴露一个值得应用 ,一般来说直接写个单例,将这个成员变量的值暴露出去就ok了,但是当时突然灵机一动(现在回想是个多余的想法),想到handle源码里面有使用过Th ...
随机推荐
- 异步函数Demo
private static async Task<String> IssueClientRequestAsync(string serverName, string message) { ...
- unomp 矿池运行问题随记
经过大量的实践,遇到的问题或经验如下: 1.单机运行多矿池时,单机CPU核心数 成为性能瓶颈,运行两个月后,有部分用户反映 矿机速率只有以前的一半. 2.Dash 等可以自行报块的矿池,每个块的股份比 ...
- ndk学习之C语言基础复习----基本数据类型、数组
关于NDK这个分类在N年前就已经创建了,但是一直木有系统的记录其学习过程,当然也没真正学会NDK的技术真谛,所以一直也是自己的一个遗憾,而如今对于Android程序员的要求也是越来越高,对于NDK也是 ...
- __builtin_ _Find_first()
•int __builtin_ffs (unsigned int x) 返回x的最后一位1的是从后向前第几位,比如7368(1110011001000)返回4. •int __builtin_clz ...
- python 中 open与with open 的区别
读写文件是最常见的IO操作.Python内置了读写文件的函数,用法和C是兼容的. 读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘, ...
- HDU 6415 Rikka with Nash Equilibrium (计数DP)
题意:给两个整数n,m,让你使用 1 ~ n*m的所有数,构造一个矩阵n*m的矩阵,此矩阵满足:只有一个元素在它的此行和此列中都是最大的,求有多种方式. 析:根据题意,可以知道那个元素一定是 n * ...
- unity vulkan snapdragon profiler
your device does not match the hardware requirements of this application 遇到上面那个warning 跟了下 是vulkan c ...
- SpringMVC 请求调用过程
1.spring mvc请所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责负责对请求进行真正的处理工作. 2.DispatcherServlet查询一个或多个Han ...
- Appium Python测试环境搭建
详细参考地址:https://www.cnblogs.com/amoyshmily/p/10500687.html 1,Appium安装:https://github.com/appium/appiu ...
- scrollTop([val])
scrollTop([val]) 概述 获取匹配元素相对滚动条顶部的偏移.广州大理石机械构件 此方法对可见和隐藏元素均有效. 参数 valString, NumberV1.2.6 设定垂直滚动条值 示 ...