一文搞懂ThreadLocal原理

2023年 9月 8日 21.7k 0

大家好,我是了不起。

ThreadLocal相信大家都用过,但你知道他的原理吗,今天了不起带大家学习ThreadLocal。

ThreadLocal是什么

在多线程编程中,经常会遇到需要在不同线程中共享数据的情况。通常情况下,为了保证线程安全,我们需要使用锁或其他同步机制。然而,有些情况下,我们希望在每个线程中都有一份独立的数据副本,这就是ThreadLocal派上用场的地方。

ThreadLocal翻译过来就是线程本地,也就是本地线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal提供了一种机制,允许我们为每个线程创建独立的变量,每个线程都可以独立访问自己的变量,而不会干扰其他线程的数据。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量,各个线程间互不影响,从而实现线程安全。

ThreadLocal的原理

ThreadLocal的原理涉及到两个重要概念:ThreadLocal实例和ThreadLocalMap。

1. ThreadLocal实例

每个ThreadLocal对象实际上是一个容器,用于存储线程本地的变量副本。每个线程都可以拥有自己的ThreadLocal实例,这些实例可以存储不同的数据,互相之间互不影响。

2. ThreadLocalMap

ThreadLocalMap是ThreadLocal的底层数据结构,它是一个哈希表。每个线程都有一个与之相关联的ThreadLocalMap,用于存储该线程所拥有的ThreadLocal实例以及对应的值。ThreadLocalMap中的键是ThreadLocal实例,值是该线程对应ThreadLocal实例的变量副本。

当我们调用ThreadLocal的set()方法设置值时,实际上是在当前线程的ThreadLocalMap中以ThreadLocal实例为键,将值存储在对应的位置。而调用get()方法时,则是从当前线程的ThreadLocalMap中根据ThreadLocal实例获取对应的值。

ThreadLocal怎么用,应用场景

public static void main(String[] args) {
   IntStream.range(1, 5).forEach(i -> new Thread(() -> {
       // 设置线程中本地变量的值
       threadLocal.set("thread-" + i);
       // 打印当前线程中本地内存中本地变量的值
       System.out.println(threadLocal.get());
       // 清除本地内存中的本地变量
       threadLocal.remove();
       // 打印本地变量
       System.out.println("thread-" + i + " after remove: " + threadLocal.get());
   }).start());
}
/*
thread-1
thread-4
thread-4 after remove: null
thread-2
thread-3
thread-2 after remove: null
thread-1 after remove: null
thread-3 after remove: null
*/

从结果可以看到,每一个线程都有各自的值,并且互不影响。

应用场景

  • 用户访问之后,在本地线程保存用户的身份信息,在本次访问过程中,可以随时获取用户的身份信息以验证身份。
  • 在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
  • 数据库连接管理:每个线程可以拥有自己的数据库连接,避免了线程之间的数据库连接混淆。
  • 用户身份管理:在Web应用中,可以将用户身份信息存储在ThreadLocal中,以便在整个请求处理过程中方便地访问。
  • 事务管理:将事务状态存储在ThreadLocal中,确保每个线程都能独立管理自己的事务状态。
  • ThreadLocal源码分析

    先看一下 ThreadLocal 和 Thread 的关系

    图片图片

    Thread类中有一个threadLocals属性,是ThreadLocal内部类ThreadLocalMap类型的变量,ThreadLocalMap可以看作是一个HashMap,其内部有一个内部类为 Entry,继承了WeakReference,value是Object类型的值。

    大致了解了Thread和ThreadLocal的关系之后,看一下Thread Local的源码:我们只要看其主要的几个方法,就可以完全了解ThreadLocal的原理了。

    set方法

    public void set(T value) {
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 通过当前线程获取线程中的ThreadLocal.ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        if (map != null)
            // map不为空,则直接赋值
            map.set(this, value);
        else
            // map为空,则创建一个ThreadLocalMap对象
            createMap(t, value);
    }
    // 根据提供的线程对象,和指定的值,创建一个ThreadLocalMap对象
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    // threadLocals是Thread类的一个属性
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    /*
    Thread 类 182行
     // ThreadLocal values pertaining to this thread. This map is maintained by the ThreadLocal class.
     与该线程有关的ThreadLocal值。这个映射由ThreadLocal类维护
        ThreadLocal.ThreadLocalMap threadLocals = null;
    */

    get方法

    // ThreadLocalMap中的内部类,存放key,value
    static class Entry extends WeakReference

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论