【Java】子线程共享父线程数据

文章介绍了在Java多线程环境下,如何使用ThreadLocal和InheritableThreadLocal来实现线程间局部变量的共享。ThreadLocal的变量不会被父子线程共享,而InheritableThreadLocal则允许子线程继承父线程的变量。通过创建子线程时的机制,如果设置inheritThreadLocals为true,子线程会复制父线程的inheritableThreadLocals,从而实现变量的继承。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【记录】子线程如何共享父线程的局部变量

ThreadLocal

在多线程中,存储线程的局部变量一般是采用ThreadLocal来实现,并且每个线程之间的ThreadLocal是不共享的,包括父子线程,下面来看一个例子:

public static void main(String[] args) {
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
        threadLocal.set("父线程main");
        // 在父线程main中创建一个子线程,并访问父线程ThreadLocal中的数据
        new Thread( () -> {
            String var = threadLocal.get();
            System.out.println("父线程的局部变量为:" + var);
        }).start();
        // 保证main线程比子线程晚结束(在IDEA中,IDEA会创建一个线程来执行main线程,所以此程序有3个线程)
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
    }

运行结果:

父线程的局部变量为:null

可以看到结果,子线程是无法通过ThreadLocal访问父线程的局部变量的。那么该怎么做呢?我们可以使用InheritableThreadLocal来实现。

InheritableThreadLocal

Thread中维护了两个线程的局部变量:

 /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

通过注释可以知道,threadLocals 是不可继承的,通过ThreadLocal来维护;而inheritableThreadLocals是可继承的,通过InheritableThreadLocal来维护。

这个类扩展了ThreadLocal以提供从父线程到子线程的值继承:当创建子线程时,子线程接收父线程具有值的所有可继承线程局部变量的初始值。

public class InheritableThreadLocal<T> extends ThreadLocal<T>

修改程序,使用InheritableThreadLocal替代ThreadLocal:

public static void main(String[] args) {
        ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
        threadLocal.set("父线程main");
        // 在父线程main中创建一个子线程,并访问父线程ThreadLocal中的数据
        new Thread( () -> {
            String var = threadLocal.get();
            System.out.println("父线程的局部变量为:" + var);
        }).start();
        // 保证main线程比子线程晚结束(在IDEA中,IDEA会创建一个线程来执行main线程,所以此程序有3个线程)
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
    }

运行结果:

父线程的局部变量为:父线程main

可以成功获取到父线程的局部变量。

原理

创建线程时,使用该构造函数:

public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

调用init方法:

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

再调用重载的init方法,其中true为inheritThreadLocals的值:

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (inheritThreadLocals && 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();
    }

看到这里的代码:

if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

可以看到,如果inheritThreadLocals为true并且父线程的inheritableThreadLocals不为空(此程序肯定不为空,通过new InheritableThreadLocal<>()创建了)时,则将父线程的inheritableThreadLocals继承给子线程的inheritableThreadLocals。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值