ThreadLocal是什么?有哪些应用场景?
大家好,我是joker,希望你快乐。
多线程情况下操作共享变量会产生线程安全问题,需要进行线程间同步,但是并不是所有的情况都是多线程去操作共享变量,有些线程是无状态的只进行操作处理,不涉及共享数据操作,所以就需要threadlocal登场了。
threadlocal是什么?
threadlocal根据命名来看,分为两部分:thread,local,线程,本地的,私有的。通过源码中的注释This class provides thread-local variables
可以看出threadlocal这个类提供线程本地变量,作为一个线程内的全局变量。
threadlocal源码分析
下面通过代码进行分析,先来看一个类图:
通过类图可以看出,ThreadLocal依赖于ThreadLocalMap,Thread通过threadLocals属性与ThreadLocalMap组成1:1的组合关系。
Thread的私有数据存储在ThreadLocalMap内。
ThreadLocal中set()方法如下:
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { map.set(this, value); } else { createMap(t, value); } }
ThreadLocal中get()方法如下
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ 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(); }
Thread中的threadLocals属性:
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap结构比较多,只取了一部分:
static class ThreadLocalMap { /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } /** * The initial capacity -- MUST be a power of two. */ private static final int INITIAL_CAPACITY = 16; /** * The table, resized as necessary. * table.length MUST always be a power of two. */ private Entry[] table;
可以看到ThreadLocalMap中维护了一个Entry类型数组,用于存储最终的数据,可以先把ThreadLocalMap认为是一个hashmap,还是有很多不同,下次再做比较。
简单做一个总结,Thread的私有变量是存储在自身上的,类型为一个ThreadLocal.ThreadLocalMap的变量,ThreadLocal是一个工具类,用于去维护线程的本地变量。
threadlocal的应用场景
通过上面的分析,可以看出ThreadLocal是一个线程安全,线程私有的,线程生命周期内的全局变量。
通过这些优势特点,可以看出ThreadLocal是线程安全的,避免某些情况需要考虑线程安全进行同步带来的性能损失。
线程生命周期内的全局变量,可以作为一个上下文信息,在整个线程的运行周期内共享数据。
threadlocal可能产生的问题,如何避免
map的key为ThreadLocal实例,key使用了弱引用,所以当把ThreadLocal实例设置为null后,没有任何强引用指向原来的ThreadLocal实例,所以ThreadLocal实例就可以顺利被GC回收。
仍然会造成内存泄漏问题,虽然key为弱引用类型,ThreadLocal能被及时回收,但是value却依然存在内存泄漏问题。ThreadLocal实例的引用设置为null后,没有任何强引用指向原来的ThreadLocal实例,ThreadLocal实例就可以被GC回收,ThreadLocal实例被回收后,value永远不会被访问到,所以存在内存泄漏问题。threadLocals对象中的Entry对象不再使用后,如果没及时清除Entry对象,而程序自身也无法通过垃圾回收机制自动清除,就可能导致内存泄漏。
- 如何解决
1. 每次用完ThreadLocal都记得调用remove()方法清除数据
2. 将ThreadLocal变量尽可能的定义成static final
- 内部优化:
1. 调用set()方法时,会采样清理,全量清理,扩容时还会继续检查
2. 调用get()方法时,没有直接命中,向后唤醒查找时会进行清理
3. 调用remove()方法时,除了清理当前Entry,还会向后继续清理
来源:http://www.cnblogs.com/Crazy_Joker
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步