当前位置: 首页 > 知识库问答 >
问题:

如何与子线程共享父线程本地对象引用?

魏澄邈
2023-03-14

用例

我有一个基于gRPC Guice的服务应用程序,其中对于特定的调用,代码流如下所示:a-

其中,A=顶级服务操作/活动类;B=创建以C类为任务的ExecutorService线程池的类;X和Y是普通类。

我希望在这些类的B、C和Y类之间有一个共享对象ContainerDoc,但不希望传递给方法参数。因此,我决定使用InheritableThreadLocal。

但我想了解如何强制将父线程本地ContainerDoc共享给子线程,以便子线程在ContainerDoc中执行的任何更新对父线程也是可见的?

  1. 重写childValue方法是否返回与父对象相同的包含对象以使其工作?(见下面的实施)。
  2. 如何确保线程安全

示例实现

class ContainerDoc implements ServiceDoc {
    private final Map < KeyEnum, Object > containerMap;

    public ContainerDoc() {
        this.containerMap = new HashMap < KeyEnum, Object > ();
        // Should it be ConcurrentHashmap to account for concurrent updates?
    }

    public < T > T getEntity(final KeyEnum keyEnum) {
        return (T) containerMap.get(keyEnum);
    }

    public void putEntity(final KeyEnum keyEnum, final Object value) {
        entities.put(keyEnum, value);
    }

    enum KeyEnum {
        Key_A,
        Key_B;
    }

}

public enum MyThreadLocalInfo {

    THREADLOCAL_ABC(ContainerDoc.class, new InheritableThreadLocal < ServiceDoc > () {

            // Sets the initial value to an empty class instance.
            @Override
            protected ServiceContext initialValue() {
                return new ContainerDoc();
            }

            // Just for reference. The below impl shows default 
            // behavior. This method is invoked when any new
            // thread is created from a parent thread.
            // This ensures every child thread will have same 
            // reference as parent.
            @Override
            protected ServiceContext childValue(final ServiceDoc parentValue) {
                return parentValue;
                // Returning same reference but I think this 
                // value gets copied over to each Child thread as 
                // separate object instead of reusing the same 
                // copy for thread-safety. So, how to ensure 
                // using the same reference as of parent thread?
            }
        }),
        THREADLOCAL_XYZ(ABC.class, new InheritableThreadLocal < ServiceDoc > () {
            ....
            ....
        });

    private final Class << ? extends ServiceDoc > contextClazz;

    private final InheritableThreadLocal < ServiceDoc > threadLocal;

    MyThreadLocalInfo(final Class << ? extends ServiceDoc > contextClazz,
        final InheritableThreadLocal < ServiceDoc > threadLocal) {
        this.contextClazz = contextClazz;
        this.threadLocal = threadLocal;
    }

    public ServiceDoc getDoc() {
        return threadLocal.get();
    }

    public void setDoc(final ServiceDoc serviceDoc) {
        Validate.isTrue(contextClazz.isAssignableFrom(serviceDoc.getClass()));
        threadLocal.set(serviceDoc);
    }

    public void clearDoc() {
        threadLocal.remove();
    }
}

客户端代码(来自子线程类或常规类

MyThreadLocalInfo.THREADLOCAL_ABC.setDoc(new ContainerDoc());
MyThreadLocalInfo.THREADLOCAL_ABC.getDoc().put(Key_A, new Object());
MyThreadLocalInfo.THREADLOCAL_ABC.clearDoc();

共有1个答案

夏学名
2023-03-14

返回相同的引用,但我认为这个值会作为单独的对象复制到每个子线程

运行时如何实例化这样一个“单独的对象”?这个理论是错误的。您的childValue()实现与默认值完全相同。

当创建一个新线程时,将根据父线程为In遗产线程本地分配一个值。ExecutorService可以有任何实现,并且您没有指定如何创建线程,但是对于您的工作方法,父线程需要设置值,创建一个新线程,然后使用该新线程执行任务。换句话说,它只能工作于非池线程。

Threadlocal是解决第三方代码中无法更改的设计缺陷的笨拙方法。即使它起作用了,这也是最后的手段——在这里,它不起作用。

根据需要将ServiceDoc作为方法或构造函数参数传递给B、C和Y。

这可能意味着X也需要传递ServiceDoc,但是,由于X-Y代码路径中没有Executor,A可以在调用X之前有条件地初始化ThreadLocal。这可能比将其作为参数传递更难看。

 类似资料:
  • 我注意到一些web框架(如Play Framework)允许您配置多个不同大小的线程池(线程池中的线程数)。假设我们在单核的单机中运行这个游戏。拥有多个线程池不会有很大的开销吗?

  • 问题内容: 我在程序开始时设置了numpy随机种子。在程序执行期间,我使用多次运行一个函数。该函数使用numpy随机函数绘制随机数。问题是获取当前环境的副本。因此,每个进程都独立运行,并且都以与父环境相同的随机种子开始。 所以我的问题是如何在父环境中与子进程环境共享numpy的随机状态?请注意,我想用于自己的工作,需要使用一个 单独的类 并在该类中单独进行操作。我尝试使用来共享随机状态,但是似乎事

  • 我创建了一个可运行的类a,它为我执行一些任务。我使用ExecutorService提交这个类,以便并行执行这些任务。 可运行类A调用另一个对象B,该对象发送一个AsyncFuture请求(future.get()one)。 我将可运行类A的单独对象提交给ExecutorService,但是,类B的对象由bean(单例)引用。这会导致线程执行出现问题吗? 我注意到类A的一些对象没有被任何线程执行。

  • 在学习基本的线程管理时,我发现很难理解书中的这些行(粗体)。 一旦您启动了线程,您需要明确地决定是等待它完成(通过与它连接--参见第2.1.2)还是让它自己运行(通过分离它--参见第2.1.3)。如果在std::Thread对象被销毁之前没有做出决定,那么程序将被终止(std::Thread析构函数调用std::Terminate())。因此,即使存在异常,也必须确保线程正确连接或分离。有关处理此

  • 问题内容: 我正在为我的ubuntu服务器(针对我的多客户端匿名聊天程序)实现一种简单的线程池机制,并且需要使我的工作线程进入睡眠状态,直到需要执行一项工作(以函数指针和参数的形式) 。 我当前的系统即将关闭。我(工人线程正在)问经理是否有工作可用,以及是否有5毫秒没有睡眠。如果存在,请将作业添加到工作队列中并运行该函数。糟糕的循环浪费。 什么我 喜欢 做的是做一个简单的事件性的系统。我正在考虑有

  • 这是一个关于Java中多线程的初学者问题。 根据我的理解,当创建多个(用户)线程来运行程序或应用程序时,就没有父线程和子线程的概念。它们都是独立的用户线程。 因此,如果主线程完成执行,那么另一个线程(Thread2)仍将继续执行,因为在Thread2的执行线程完成之前,它不会被JVM杀死(https://docs.oracle.com/javase/6/docs/api/java/lang/Thr