ThreadLocal内部结构深度解析(Ⅰ)

发布于:2025-07-15 ⋅ 阅读:(15) ⋅ 点赞:(0)

目录

使用ThreadLocal

 例子

内部结构分析

源码解析

 图示详解


ThreadLocal是Java中一个非常重要且常用的线程局部变量工具类,它使得每个线程可以独立地持有自己的变量副本,而不是共享变量,解决了多线程环境下变量共享的线程安全问题。下面我将从多个维度深入分析ThreadLocal的内部结构和工作原理。

使用ThreadLocal

// 1. 初始化:创建ThreadLocal变量
private static ThreadLocal<T> threadLocal = new ThreadLocal<>();

// 2. 设置值:为当前线程设置值
threadLocal.set(value);  // value为要存储的泛型对象

// 3. 获取值:获取当前线程的值
T value = threadLocal.get();  // 返回当前线程存储的值

// 4. 移除值:清除当前线程的ThreadLocal变量(防止内存泄漏)
threadLocal.remove();

【注】使用时,通常将ThreadLocal声明为static final以保证全局唯一性

private static ThreadLocal<T> threadLocal = ThreadLocal.withInitial(() -> initialValue);

【注:】 withInitial里面放的是任何能够返回 T 类型实例的 Lambda / Supplier
只要 Supplier 的逻辑最终能 new(或从缓存、工厂、单例池等)拿出一个 T,就合法。

 例子

package com.qcby.test;

import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadLocalTest {
    private List<String> messages = new ArrayList<>();

    public static final ThreadLocal<ThreadLocalTest> holder = ThreadLocal.withInitial(ThreadLocalTest::new);

    public static void add(String message) {
        holder.get().messages.add(message);
    }

    public static List<String> clear() {
        List<String> messages = holder.get().messages;
        holder.remove();
        return messages;
    }

    public static void main(String[] args) throws InterruptedException {
        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(10);

        // 提交10个任务
        for (int i = 0; i < 10; i++) {
            final int threadId = i;
            executor.submit(() -> {
                    ThreadLocalTest.add("线程" + threadId + "的消息" );
                // 打印当前线程的消息
                System.out.println("线程" + threadId + "的消息列表: " + holder.get().messages);
                // 清除当前线程的ThreadLocal
                ThreadLocalTest.clear();
            });
        }

        // 关闭线程池
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.SECONDS);

        // 主线程检查自己的ThreadLocal(应该是空的)
        System.out.println("主线程的消息列表: " + holder.get().messages);
    }
}

内部结构分析

根据这里get的源码追溯分析:

追溯到:

源码解析
/**
 * 获取当前线程的ThreadLocal变量值
 */
public T get() {
    // 1. 获取当前线程对象
    Thread t = Thread.currentThread();
    
    // 2. 获取当前线程的ThreadLocalMap(线程私有数据存储结构)
    ThreadLocalMap map = getMap(t);
    
    // 3. 如果map已存在
    if (map != null) {
        // 3.1 以当前ThreadLocal实例为key(也就是代码中的holder),获取对应的Entry
        ThreadLocalMap.Entry e = map.getEntry(this);
        
        // 3.2 如果Entry存在
        if (e != null) {
            // 3.2.1 强转为泛型类型并返回值
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    
    // 4. 如果map不存在或未找到值,初始化并返回默认值
    return setInitialValue();
}

/**
 * 获取线程的ThreadLocalMap(实际是Thread类的threadLocals字段)
 */
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals; // 直接返回线程对象的成员变量
}

/**
 * 初始化值并存入ThreadLocalMap
 */
private T setInitialValue() {
    // 1. 获取初始值(子类可重写initialValue()方法)
    T value = initialValue();
    
    // 2. 获取当前线程
    Thread t = Thread.currentThread();
    
    // 3. 获取线程的ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    
    // 4. 如果map已存在,直接设置值
    if (map != null) {
        map.set(this, value);
    } else {
        // 5. 如果map不存在,创建新map并存入初始值
        createMap(t, value);
    }
    
    // 6. 返回初始值
    return value;
}

/**
 * 创建线程的ThreadLocalMap并存入第一个值
 */
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

/**
 * 默认初始值实现(可被withInitial覆盖)
 */
protected T initialValue() {
    return null; // 默认返回null
}
 图示详解

所以执行结果:

可以看见一个线程中只有一个信息,而不是它们统一堆砌在一起,原因就是底层是每个线程创建了一个Map对象,每个Map的value就是存入的messages本质是对象,也就是T--ThreadLocalTest对象们,并且它们Map中的Entry中的Key值都是一样的,都是这个ThreadLocal,也就是holder。

注】并不是每个线程的Map只能存放一个value对象,是这里我展示的例子里,一个线程只存了一条,完全可以存入很多条消息,然后add()时就会累加在Map已经创建好的Entry后面也就是:

当然既然是Map,存储Entry就涉及Hash了,这个以后再详谈。


网站公告

今日签到

点亮在社区的每一天
去签到