概述
翻译过来的大概意思是:ThreadLocal类用来提供线程内部的局部变量。这些变量在多线程环境下访问
(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量。
总结:ThreadLocal不是为了解决多线程访问共享变量,而是为每个线程创建一个独立的变量副本,提供了保持对象的方法和避免参数传递的复杂性。
实现原理
ThreadLocal可以看做是一个容器,容器里面存放着属于当前线程的变量。ThreadLocal类提供了四个对外开放的接口方法,这也是用户操作ThreadLocal类的基本方法:
- void set(Object value) 设置当前线程的线程局部变量的值。
- public Object get() 该方法返回当前线程所对应的线程局部变量。
- public void remove() 将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
- protected Object initialValue() 返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次,ThreadLocal中的缺省实现直接返回一个null。
ThreadLocal内部如何为每一个线程维护变量副本呢? 其实在ThreadLocal类中有一个静态内部类ThreadLocalMap,用键值对的形式存储每一个线程的变量副本,其中key为当前ThreadLocal对象,而value对应线程的变量副本。每个线程可能存在多个ThreadLocal。
----------------------------------------------------------------------------------------------------------------------------------------------
源代码解析:
/** * 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(ThreadLocalMap为线程的一个属性) ThreadLocalMap map = getMap(t); if (map != null) { /* ThreadLocalMap的key为ThreadLocal对象,value为对应的变量副本 此处获取当前ThreadLocal对象对应的Entry */ ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { // 获取变量副本并返回 @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } // 如果ThreadLocalMap为空,则初始化 return setInitialValue(); } /** * 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) { /* 获取当前线程的ThreadLocalMap(若不存在则创建), 并将value放入ThreadLocalMap中 */ Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } /** * Removes the current thread's value for this thread-local * variable. If this thread-local variable is subsequently * {@linkplain #get read} by the current thread, its value will be * reinitialized by invoking its {@link #initialValue} method, * unless its value is {@linkplain #set set} by the current thread * in the interim. This may result in multiple invocations of the * {@code initialValue} method in the current thread. * * @since 1.5 */ public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); } /** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the map */ void createMap(Thread t, T firstValue) { /* 为当前线程创建一个ThreadLocalMap对象并赋值给threadLocals, 并存入第一个值(this表示当前ThreadLocal对象) */ t.threadLocals = new ThreadLocalMap(this, firstValue); } /** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @return the initial value */ private T setInitialValue() { // 默认初始化为null T value = initialValue(); /* 获取当前线程的ThreadLocalMap(若不存在则创建), 并将value放入ThreadLocalMap中 */ Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
上述是在ThreadLocal类中的几个主要的方法,他们的核心都是对其内部类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. * * map中的每个节点Entry,其键key是ThreadLocal并且还是弱引用,这也导致了后续会产生内存泄漏问题的原因 */ 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. * * 初始化容量为16,以为对其扩充也必须是2的指数 */ private static final int INITIAL_CAPACITY = 16; /** * The table, resized as necessary. * table.length MUST always be a power of two. * * 真正用于存储线程的每个ThreadLocal的数组,将ThreadLocal和其对应的值包装为一个Entry。 */ private Entry[] table; // ...... 其他操作和Map类似
总之,为不同线程创建不同的ThreadLocalMap,以线程本身为区分点,每个线程之间其实没有任何的联系。说是存放了变量的副本,其实可以理解成为每个线程单独new了一个对象。
对象存放在哪里
在Java中,栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。而堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。
问:那么是不是说ThreadLocal的实例以及其值存放在栈上呢?
其实不是,因为ThreadLocal实例实际上也是被其创建的类持有(更顶端应该是被线程持有)。而ThreadLocal的值其实也是被线程实例持有。它们都是位于堆上,只是通过一些技巧将可见性修改成了线程可见。
【注意:变量是保存在线程中的,而不是保存在ThreadLocal变量中】
真的只能被一个线程访问么
上面提到了ThreadLocal只对当前线程可见,是不是说ThreadLocal的值只能被一个线程访问呢?使用InheritableThreadLocal可以实现多个线程访问ThreadLocal的值。如下,我们在主线程中创建一个InheritableThreadLocal的实例,然后在子线程中得到这个InheritableThreadLocal实例设置的值。
private void testInheritableThreadLocal() { final ThreadLocal threadLocal = new InheritableThreadLocal(); threadLocal.set("droidyue.com"); Thread t = new Thread() { @Override public void run() { super.run(); Log.i(LOGTAG, "testInheritableThreadLocal = " + threadLocal.get()); } }; t.start(); }
上面的代码输出的日志信息为:
testInheritableThreadLocal = droidyue.com
//Thread.java private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) { //code goes here if (parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; /* Set thread ID */ tid = nextThreadID(); }
上面代码就是在线程创建的时候,复制父线程的inheritableThreadLocals的数据。
使用场景
实现线程安全,非线程安全的对象使用ThreadLocal之后就会变得线程安全,因为每个线程都会有一个对应的实例副本。
承载一些线程相关的数据,避免在方法中来回传递参数。
相关推荐
ThreadLocal是Java中用于线程局部变量的一个工具类,它的工作原理主要体现在如何在不同的线程之间隔离变量的副本,确保每个线程拥有自己的独立数据。这个设计模式在多线程编程中尤其有用,因为它避免了传统的同步...
深入理解ThreadLocal工作原理及使用示例 ThreadLocal是Java提供的一种解决多线程程序并发问题的工具类,自JDK1.2版本以来提供了java.lang.ThreadLocal类。ThreadLocal的主要作用是为每个使用该变量的线程提供独立的...
### 一、ThreadLocal工作原理 ThreadLocal通过内部的Map存储每个线程的变量副本。当线程首次调用get()方法时,如果没有对应的副本,ThreadLocal会调用initialValue()方法初始化副本。这个方法默认返回null,但通常...
ThreadLocal 的原理、源码深度分析及使用 ThreadLocal 是 Java 语言中的一种机制,用于实现线程本地存储,能够帮助开发者在多线程环境下解决变量访问安全的问题。下面将对 ThreadLocal 的原理、实现机制、使用方法...
ThreadLocal 工作原理: - 每个 ThreadLocal 实例都有一个 ThreadLocalMap,它不是 HashMap 而是基于 Entry 实现的。 - Entry 使用弱引用作为键,这样当 ThreadLocal 变量没有其他强引用时,Entry 会被垃圾回收,...
ThreadLocal的工作原理主要基于以下几点: - **内部类ThreadLocalMap**:ThreadLocal在每个线程内部维护了一个名为ThreadLocalMap的哈希映射表,这个表的键是ThreadLocal对象,值是线程局部变量的实际值。这样,每...
其他说明:为了更好地理解ThreadLocal的工作机制,建议实际动手尝试文中提供的实例代码,同时注意不同版本JDK之间的差异可能会导致部分细节有所变化。对于追求高性能并发应用开发的技术人员而言,了解和运用...
ThreadLocal的原理是什么,使用场景有哪些.md
首先,ThreadLocal原理是基于每个线程创建一个私有的数据存储结构(ThreadLocalMap),使得线程可以拥有自己独立的数据副本,而不会与其他线程共享。这意味着,即使多个线程访问相同的变量,它们也各自拥有这个变量...
在Java中,ThreadLocal的工作原理是为每个线程创建一个单独的存储空间,每个线程可以独立地读写这个变量,而不会影响其他线程。当我们创建一个新的ThreadLocal实例时,它并不会立即分配内存,而是等到线程第一次调用...
### 理解ThreadLocal原理 ThreadLocal内部通过一个ThreadLocalMap来存储每个线程的副本。这个Map的键是ThreadLocal实例,值是线程的局部变量。每个线程都有自己的ThreadLocalMap,存储在Thread类的成员变量中。 ##...
通过ThreadLocal对象定位到线程:Thread.currentThread()通过ThreadLocal对象拿到所在的ThreadLocalMap: T
下面将详细介绍ThreadLocal的工作原理、使用方法以及在Android中的实际应用。 ### 1. ThreadLocal工作原理 ThreadLocal内部实现了一个HashMap,用于存储每个线程与对应的变量副本之间的映射关系。当我们创建一个新...
下面将详细探讨 ThreadLocal 的工作原理、使用方法以及适用场景。 1. **ThreadLocal 工作原理** - 每个 `ThreadLocal` 实例在每个线程中都有一个对应的存储槽,这个存储槽存储的是变量的副本。当创建一个新的 `...
ThreadLocal原理分析图,visio软件打开
因此,理解ThreadLocal的工作原理并谨慎使用是非常重要的。 在提供的"ThreadLocal示例"压缩包中,可能包含了一些具体的代码示例,展示如何在实际项目中运用ThreadLocal。通过查看这些示例,你可以更深入地理解...
ThreadLocal的工作原理基于Java的Thread类。在ThreadLocal中,每个实例都有一个内部Map,这个Map存储了键值对,键是ThreadLocal实例,值就是线程本地的变量副本。在JDK 8之前,这个内部Map是`ThreadLocal....
at 中专门为每一个 web 应用...理解 `ThreadLocal` 的工作原理以及它如何与类加载器交互,是避免此类问题的关键。在实际开发中,应当养成良好的编程习惯,如使用后及时清理 `ThreadLocal` 变量,以防止内存资源的浪费。