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);
}
最新文章
- php基础之gd图像生成、缩放、logo水印和简单验证码实现
- CentOs下jdk的安装
- [Python] Search navigation in Pycharm
- NServiceBus教程-消息传递与处理
- vs 行数
- [React Fundamentals] Component Lifecycle - Mounting Usage
- (转载)PHP substr(),mb_substr()及mb_strcut的区别和用法
- SPOJ PGCD (mobius反演 + 分块)
- CDZSC_2015寒假新人(1)——基础 i
- 运用JavaScript构建你的第一个Metro式应用程序(on Windows 8)(一)
- Sql server 事务的两种用法
- 洛谷 [P4016] 负载平衡问题
- 重新初始化VS2010
- springMVC的配置与使用
- 如何编写JQuery 插件详解
- Python匿名函数(lambda)
- Ubuntu下无法输入中文问题解决
- leetcode python 007
- mysql连接数设置操作(Too many connections)及设置md5值的加密密码
- maven学习(4)-Maven 构建Web 项目
热门文章
- HAL UART DMA 数据收发
- Redis长短链接的区别
- python+Appium自动化:app滑动操作swipe
- Hive的JDBC连接
- MySQL主从同步、读写分离配置步骤、问题解决笔记
- Java中的集合Collection、Iterator和Foreach用法(一)
- BZOJ 3514: Codechef MARCH14 GERALD07加强版 (LCT维护最大生成树+主席树)
- Redis:RedisHelper(5)
- Luogu P1066 2^k进制数 组合数学
- Vue + Vuex 简单使用