
本文共 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 防止方法:
- 当不再使用存储的某一个值时一定要使用remove()方法进行移除。
- 多线程中的使用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()方法去移除这个变量,否则可能会导致内存泄漏。
发表评论
最新留言
关于作者
