What's ThreadLocal?
ThreadLocal 是 Java 中的一个类,用于实现线程局部变量。每个 ThreadLocal 对象都可以存储一个线程本地的变量,这个变量对于其他线程是不可见的,每个线程都有自己独立的副本。这样可以避免线程间的数据共享和竞争条件,提高了线程安全性。
ThreadLocal的特点和用途
- 每个线程都有自己独立的变量副本,互不干扰,避免了线程间的数据共享问题。
- 可以在多线程环境下实现线程安全的单例模式。
- 可以用于避免传递参数的繁琐,将变量保存在 ThreadLocal 中,不同方法直接通过 ThreadLocal 获取即可。
- 可以用于实现线程上下文信息的传递,比如用户身份信息、事务信息等。
需要注意的是,使用 ThreadLocal 时要避免内存泄漏问题,因为 ThreadLocal 中的变量是和线程绑定的,如果不及时清理,可能会导致内存泄漏。通常在使用完 ThreadLocal 后要调用 remove 方法来清理变量。
For example
以下是一个简单的示例代码,演示如何在多线程环境下使用 ThreadLocal:
public class ThreadLocalDemo {
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
public static void main(String[] args) {
Runnable task = () -> {
// 获取当前线程的ThreadLocal变量, 并 对其进行操作int value = threadLocal.get();
Integer value = threadLocal.get();
value++;
threadLocal.set(value);
System.out.println("Thread "
+ Thread.currentThread().getName() + ": " + value);
};
// 创建多个线程并启动
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
Thread thread3 = new Thread(task);
thread1.start();
thread2.start();
thread3.start();
}
}
/** Output:
Thread Thread-1: 1
Thread Thread-2: 1
Thread Thread-0: 1
**///~
ThreadLocal 对象 threadLocal,初始值为 0。然后创建了三个线程,每个线程执行一个任务,任务是获取当前线程的 ThreadLocal 变量,并对其进行自增操作。最后输出每个线程的执行结果。 运行该示例代码,可以看到每个线程都有自己独立的 ThreadLocal 变量副本,并且不会相互影响。
ThreadLocalMap 对象
ThreadLocalMap是ThreadLocal类中的一个内部静态类,用于存储每个线程对应的 ThreadLocal变量的值。ThreadLocalMap内部使用了一个Entry数组来存储键值对,其中键是ThreadLocal对象,值是对应的线程本地变量的值。
在ThreadLocalMap中,使用 ThreadLocal对象的hashCode方法计算出的哈希值作为索引位置存储对应的值。由于哈希冲突是不可避免的,可能会出现多个ThreadLocal对象计算出的哈希值相同的情况,导致存储位置冲突(散列碰撞)。
为了解决哈希冲突, ThreadLocalMap中采用了 线性探测法 来处理。具体来说,如果计算出的索引位置已经被占用,就会顺序向后查找空闲的位置来存储数据。这样可以避免数据覆盖的问题,确保每个ThreadLocal对象对应的值都能被正确存储和访问。
另外,ThreadLocalMap中的Entry数组会动态扩容,以减少哈希冲突的概率。当数组长度达到一定阈值时,会重新计算每个ThreadLocal对象的哈希值并重新分配存储位置,以保证存储效率和性能。总的来说,ThreadLocalMap在处理哈希冲突时采用了线性探测法和动态扩容的策略,确保数据的正确性和高效性。
ThreadLocalMap 内部类Entry
/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
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;
}
}
... ...
}
Entry 类继承自WeakReference<ThreadLocal<?>>,表示对 ThreadLocal 对象的弱引用。在 Entry 对象中, value 字段存储了线程本地变量的值,而 key 字段则 存储了对应的 ThreadLocal 对象。
当一个线程访问 ThreadLocal 对象时,实际上是通过 ThreadLocalMap 中的 Entry 对象来获取和设置线程 本地变量的值。 Entry 对象中的 key 字段指向对应的 ThreadLocal 对象, value 字段存储了线程本地变 量的值。
通过 Entry 对象的方式, ThreadLocalMap 实现了将线程本地变量与线程关联起来的功能,确保每个线程都可以独立地访问和修改自己的线程本地变量。这种机制可以避免多线程并发访问时的数据混乱和竞争条件。
ThreadLocal set()方法
当调用ThreadLocal的set方法时,实际上是 通过Thread类的threadLocals字段获取当前线程的ThreadLocalMap对象,然后在 ThreadLocalMap中通过ThreadLocal对象作为键来存储对应的值。具体的存储过程是通 过ThreadLocal对象的hashCode方法计算出 在Entry数组中的索引位置,然后将值存储在该位置。
See also
- What's WeakReference class?
- see java.lang.ref.WeakReference 类
- java.lang.ThreadLocal 类