线程之间的数据传输


🧠 InheritableThreadLocal 原理详解笔记

✅ 1. 本质区别:ThreadLocal vs InheritableThreadLocal

  • ThreadLocal:线程本地变量,仅当前线程可访问,不会传递给子线程。

  • InheritableThreadLocal:可继承的线程本地变量,在创建子线程时会将父线程的值复制过去


🔑 2. 子线程能访问父线程值的真正原因

当你写下下面这行代码:

private static final InheritableThreadLocal<Map<String, Object>> context = new InheritableThreadLocal<>();

你其实是声明了一个名为 context 的变量,它指向一个 InheritableThreadLocal 实例。

那么关键点来了:

💡 子线程在创建时,JVM 会把父线程的 ThreadLocalMap 中所有的 InheritableThreadLocal 的值复制一份到子线程自己的 ThreadLocalMap

具体表现为:

  • 子线程会拥有自己的 ThreadLocalMap

  • 这个 map 的 key 是父线程的 InheritableThreadLocal 对象实例(如 context

  • value 是父线程 context.get() 的值,或者 childValue() 方法的返回结果(如果你覆写了它)。


💬 3. 为什么子线程可以直接用 context.get()

因为:

  • context 是一个变量,指向同一个 InheritableThreadLocal 实例。

  • 子线程调用 context.get(),本质是在用这个 InheritableThreadLocal 实例当作 key,从自己的 ThreadLocalMap 中获取值。

  • 这个 key(也就是 context)在子线程启动时就被复制过来了,所以能取到。

所以变量名可以一样,是因为它本来就是同一个实例引用。不是变量名继承了,而是 key 是同一个对象。


🧬 4. 可视化理解(简化图)

假设你在父线程这样设置:

context.set(Map.of("client", xxx, "MAGIC", yyy));

子线程启动时,JVM 会自动做:

子线程的 threadLocals:
  key: 父线程的 context(InheritableThreadLocal 实例)
  value: Map.of("client", xxx, "MAGIC", yyy)

于是子线程调用:

context.get();  // 实际上是从自己的 threadLocals 中用 context 作为 key 取值

public class InheritableThreadLocalDemo {

    // 创建一个 InheritableThreadLocal 变量
    private static InheritableThreadLocal<String> context = new InheritableThreadLocal<>();

    public static void main(String[] args) {
        // 父线程设置 context 的值
        context.set("Parent Thread Context");

        // 打印父线程的 context 值
        System.out.println("Parent Thread context: " + context.get());

        // 启动一个子线程
        Thread childThread = new Thread(new Runnable() {
            @Override
            public void run() {
                // 子线程访问父线程的 context 值
                System.out.println("Child Thread context before change: " + context.get());

                // 子线程修改自己的 context 值
                context.set("Child Thread Context");

                // 子线程修改后的 context 值
                System.out.println("Child Thread context after change: " + context.get());
            }
        });

        // 启动子线程
        childThread.start();

        // 主线程继续使用自己的 context 值
        try {
            childThread.join();  // 等待子线程执行完毕
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 打印父线程的 context 值
        System.out.println("Parent Thread context after child thread starts: " + context.get());
    }
}
 

补充一点父子线程之间数据的传输除了这个之外:还可与使用juc提供的一些集合如:


✅ 常用的 JUC 集合类(适用于父子线程数据传输)

1. BlockingQueue 接口及其实现类

这些是最常用的线程间通信工具,尤其适合生产者-消费者模型,但也适合父子线程的数据传输。

  • ArrayBlockingQueue

  • LinkedBlockingQueue

  • PriorityBlockingQueue

  • DelayQueue

  • SynchronousQueue

示例:

BlockingQueue<String> queue = new LinkedBlockingQueue<>();

Thread child = new Thread(() -> {
    try {
        String data = queue.take(); // 子线程等待父线程传数据
        System.out.println("子线程收到数据:" + data);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

child.start();

queue.put("父线程传的数据"); // 父线程传数据到队列中

2. ConcurrentHashMap / ConcurrentSkipListMap

如果父子线程要共享结构化数据,比如键值对,可以使用这些线程安全的 Map:

ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();

Thread child = new Thread(() -> {
    String value = map.get("key");
    System.out.println("子线程读取到数据:" + value);
});

map.put("key", "value from parent"); // 父线程写入
child.start();

3. CopyOnWriteArrayList / CopyOnWriteArraySet

用于读多写少的场景,适合子线程读取父线程准备的数据。

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("parent data");

Thread child = new Thread(() -> {
    System.out.println("子线程读取到列表:" + list);
});
child.start();

4. Exchanger

如果父子线程是一对一交换数据的场景,Exchanger 非常合适。

Exchanger<String> exchanger = new Exchanger<>();

Thread child = new Thread(() -> {
    try {
        String data = exchanger.exchange("子线程数据");
        System.out.println("子线程收到:" + data);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

child.start();

String fromChild = exchanger.exchange("父线程数据");
System.out.println("父线程收到:" + fromChild);

总结:适用于父子线程通信的 JUC 集合

类型特点适用场景
BlockingQueue 系列支持阻塞的线程安全队列生产者-消费者、父子线程通信
ConcurrentHashMap高并发读写的线程安全 Map父子线程共享结构化数据
CopyOnWriteArrayList读多写少,写操作复制父写子读,数据量不大
Exchanger线程对之间互相交换数据一对一数据交换

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值