参考:https://www.jianshu.com/p/377bb840802f

   https://www.cnblogs.com/dreamroute/p/5034726.html

ThreadLocal是什么  线程局部变量

使用场景:变量是同一个,但是每个线程都使用同一个初始值,也就是使用同一个变量的一个新的副本。这种情况之下ThreadLocal就非常使用,比如说DAO的数据库连接,我们知道DAO是单例的,那么他的属性Connection就不是一个线程安全的变量。而我们每个线程都需要使用他,并且各自使用各自的。这种情况,ThreadLocal就比较好的解决了这个问题。

public final class ConnectionUtil {

    private ConnectionUtil() {}

    private static final ThreadLocal<Connection> conn = new ThreadLocal<>();

    public static Connection getConn() {
Connection con = conn.get();
if (con == null) {
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection("url", "userName", "password");
conn.set(con);
} catch (ClassNotFoundException | SQLException e) {
// ...
}
}
return con;
} }

Threadlocal是一个数据结构,有点像HashMap,可以保存"key : value"键值对,但是一个ThreadLocal只能保存一个,并且各个线程的数据互不干扰。

看源码:

public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}//set方法将threadLocals保存到threadLocals //当我们调用get方法的时候,其实每个当前线程中都有一个ThreadLocal。
//每次获取或者设置都是对该ThreadLocal进行的操作,是与其他线程分开的。
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;
}

ThreadLoalMap

初始化大小为16的Entry数组,Entry对象用来保存每一个key-value键值对,key为ThreadLoad对象。

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();
}
1、如果当前位置是空的,那么正好,就初始化一个Entry对象放在位置i上;
2、不巧,位置i已经有Entry对象了,如果这个Entry对象的key正好是即将设置的key,那么重新设置Entry中的value;
3、很不巧,位置i的Entry对象,和即将设置的key没关系,那么只能找下一个空位置;

内存泄露

static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value; Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}

ThreadLocalMap中key被保存到WeakReference中,ThreadLocal在没有外部强引用时,发生GC时会被回收,如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,发生内存泄露。

避免:

既然已经发现有内存泄露的隐患,自然有应对的策略,在调用ThreadLocal的get()、set()可能会清除ThreadLocalMap中key为null的Entry对象,这样对应的value就没有GC Roots可达了,下次GC的时候就可以被回收,当然如果调用remove方法,肯定会删除对应的Entry对象。

如果使用ThreadLocal的set方法之后,没有显示的调用remove方法,就有可能发生内存泄露,所以养成良好的编程习惯十分重要,使用完ThreadLocal之后,记得调用remove方法。

最新文章

  1. 折半算法的C#实现方式-递归和非递归
  2. Import 元素 (MSBuild)
  3. Redis 数据库
  4. openstack 在线repo
  5. 无需Cygwin,如果没有在命令行,Eclipse编NDK
  6. Python学习笔记整理(三)Python中的动态类型简介
  7. ResourceString的用法
  8. inline-block代替浮动布局float:left列表布局最佳方案
  9. UVAlive 2911 Maximum(贪心)
  10. scss 初学笔记 一 变量声明 默认的样式 嵌套
  11. Ajax/XHR/HTTP/jQuery Ajax
  12. .gitignore文件不起作用的解决方法
  13. IOS开发证书常见问题
  14. day19
  15. Python Django Web开发的5个优秀好习惯
  16. 微信小程序中的循环遍历问题
  17. 读vue-0.6-text-parser.js源码
  18. Codeforces 744C Hongcow Buys a Deck of Cards 状压dp (看题解)
  19. CSS 3. 文本|字体|背景|定位
  20. NodeJS + PhantomJS 抓取页面信息以及截图

热门文章

  1. IOS CALayer基本使用 (图层)
  2. PHP 的那些错误总结
  3. javaweb基础(30)_EL函数库
  4. 问题002:我们要使用的Java是哪个版本的?什么是JVM、JRE、JDK、IDE、API?
  5. HttpServletRequest HttpServletResponse ServletException 重新打开后报红解决方法
  6. js常见问题总结归纳
  7. 51nod——2478 小b接水(预处理 思维)
  8. 第六篇:python中numpy.zeros(np.zeros)的使用方法
  9. tp3.2读取time()格式遇到的的问题(尚未解决)
  10. day 52 Django基础一之web框架的本质