ThreadLocal类
发布日期:2021-05-09 05:38:33 浏览次数:21 分类:博客文章

本文共 2508 字,大约阅读时间需要 8 分钟。

1.简介

ThreadLocal类是为了将变量与线程绑定到一起的工具类,每一个线程在ThreadLocal类中存储的变量不会被其他线程访问到,这样可以实现多线程中变量的存储,而又不会出现多线程并发导致的变量访问出错。

2.使用方法

2.1 API

  • 实例化对象: ThreadLocal
  • 存值: t.set(Object o);
  • 取值:t.get();
  • 移除存储的值:t.remove();

2.2 常见的使用方式

最常用的是在某一个上层入口类中定义一个静态的ThreadLocal变量,或者定义一个工具类,在工具类中封装一下,然后在多线程逻辑中使用set()或者get()方法。

3. 源码分析

在java7及之前,ThreadLocal类就是一个Map,它使用Thread类对象来当做key,存储的值当做value。java8之后设计改动了。Thread类内部有一个ThreadLocalMap类的变量,它的Key是ThreadLocal类对象,存储的值是value。

ThreadLocal的set()方法源码

public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }

查看源码可以看到,当我们使用ThreadLocal对象存值的时候,ThreadLocal在内部获取了当前线程对象,然后获取当前线程的ThreadLocalMap类型的变量,然后将自身对象当做key,存储的值当做value存储在Thread类中的变量threadLocals中。

ThreadLocal类的get()方法源码

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();    }private T setInitialValue() {        T value = initialValue();        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);        return value;    }

查看get()方法的源码也可以看出,当我们调用ThreadLocal的get()方法取值的时候,其实也是从当前线程对象的ThreadLocalMap类型的变量中取值的。

4. ThreadLocal的注意事项

ThreadLocal在使用中,是有可能发生内存泄漏的。

4.1 防止方法:

  1. 当不再使用存储的某一个值时一定要使用remove()方法进行移除。
  2. 多线程中的使用ThreadLocal的线程寿命很短,即没有使用线程池。

4.2 原因:

从上面的分析就可以知道,在内存中,有3个重要的变量,ThreadLocal变量对象,Thread对象,ThreadLocalMap对象,因为ThreadLocalMap对象是Thread对象中的一个属性变量,所以当线程执行结束,Thread对象被销毁的时候,ThreadLocalMap对象也会被销毁,这就是防止方法2的原理。

但假如我们使用了线程池,每个Thread对象在完成业务逻辑后并不会被销毁,而在Thread对象内部的ThreadLocalMap对象自然也不会被销毁,在ThreadLocalMap对象内部,对key也就是ThreadLocal对象的引用是弱引用,所以它一旦没有了我们在前面定义ThreadLocal对象时使用的强引用并且被垃圾回收程序发现,就会被回收。但是我们在ThreadLocalMap对象中存的value是强引用的,只要当前线程没有被销毁或者没有调用remove()方法,它几乎就是一直存在在内存当中的。之所以说是几乎,是因为在ThreadLocalMap内部,当作为key的ThreadLocal对象被回收后,再次去取值的时候,会发现key变成了null,然后就会把value也置位null,这个时候我们存储的value失去了引用,当垃圾回收系统发现它的时候,就会把它回收了。

5. 总结

当使用完ThreadLocal中的存储的变量之后,记得一定要调用remove()方法去移除这个变量,否则可能会导致内存泄漏。

上一篇:java注解
下一篇:java动态代理

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2025年03月31日 12时15分53秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章