Guice单身人士是否尊重线程限制? [英] Do Guice singletons honor thread-confinement?

查看:107
本文介绍了Guice单身人士是否尊重线程限制?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Guice以及它的单身人士是否会遵守我可能尝试设置的线程限制表示关注:

I have a concern regarding Guice and whether or not its singletons will obey thread confinement that I may try to set up:

public class CacheModule extends AbstractModule {
    @Override
    protected void configure() {
        // WidgetCache.class is located inside a 3rd party JAR that I
        // don't have the ability to modify.
        WidgetCache widgetCache = new WidgetCache(...lots of params);

        // Guice will reuse the same WidgetCache instance over and over across
        // multiple calls to Injector#getInstance(WidgetCache.class);
        bind(WidgetCache.class).toInstance(widgetCache);
    }
}

// CacheAdaptor is the "root" of my dependency tree. All other objects
// are created from it.
public class CacheAdaptor {
    private CacheModule bootstrapper = new CacheModule();

    private WidgetCache widgetCache;

    public CacheAdaptor() {
        super();

        Injector injector = Guice.createInjector(bootstrapper);

        setWidgetCache(injector.getInstance(WidgetCache.class));
    }

    // ...etc.
}

所以你可以看到,每当我们创建<$的新实例时c $ c> CacheAdaptor ,一个 CacheModule 将用于引导它下面的整个依赖树。

So as you can see, any time we create a new instance of CacheAdaptor, a CacheModule will be used to bootstrap the entire dependency tree underneath it.

如果从多个线程中调用新的CacheAdaptor(); ,会发生什么?

What happens if new CacheAdaptor(); is called from inside multiple threads?

例如:线程#1通过其无参数构造函数创建一个新的 CacheAdaptor ,而线程#2执行相同的操作。 Will Guice会为每个线程的 CacheAdaptor 提供完全相同的 WidgetCache 实例,或者Guice会提供2个不同的实例每个线程?即使 toInstance(...)应该返回相同的单例实例,我希望 - 因为模块是在2个不同的内部创建的线程 - 每个 CacheAdaptor 将收到一个不同的 WidgetCache 实例。

For example: Thread #1 creates a new CacheAdaptor via its no-arg constructor, and Thread #2 does the same thing. Will Guice provide the same exact WidgetCache instance to each thread's CacheAdaptor, or will Guice provide 2 different instances to each thread? Even though toInstance(...) is supposed to return the same singleton instance, I'm hoping - since the modules are created inside 2 distinct threads - that each CacheAdaptor will receive a different WidgetCache instance.

提前致谢!

推荐答案

这不仅仅是Guice 提供相同的单身人士同一个注入器的线程,但如果你使用 toInstance ,Guice 只能在线程中提供相同的单例。每个注入器对模块进行一次评估,你给了Guice一个实例而无法生成第二个实例。

It's not only that Guice will provide the same singleton across threads for the same injector, but Guice can only provide the same singleton across threads if you use toInstance. Modules are evaluated once per injector, and you gave Guice one instance and no way to produce a second one.

Guice不是魔法。在尝试提供Object的实例时,它需要(1)Guice友好的无参数或 @Inject -annotated构造函数; (2) Provider @Provides 方法,让你自己创建实例;或者(3)你已经创建并与 toInstance 绑定的实例,Guice重用它,因为它不知道如何创建另一个。请注意,带有 Provider 的选项2不需要保证每次都创建一个新实例,我们可以利用它来编写具有ThreadLocal缓存的Provider 。它看起来像这样:

Guice isn't magic. When trying to provide an instance of an Object, it either needs (1) a Guice-friendly no-arg or @Inject-annotated constructor; (2) a Provider or @Provides method, letting you create the instance yourself; or (3) an instance that you've already created and bound with toInstance, which Guice reuses because it doesn't know how to create another. Note that option 2, with a Provider, doesn't need to guarantee that it creates a new instance every single time, and we can exploit that to write a Provider that has a ThreadLocal cache. It would look something like this:

public class CacheModule extends AbstractModule {
    /** This isn't needed anymore; the @Provides method below is sufficient. */
    @Override protected void configure() {}

    /** This keeps a WidgetCache per thread and knows how to create a new one. */
    private ThreadLocal<WidgetCache> threadWidgetCache = new ThreadLocal<>() {
        @Override protected WidgetCache initialValue() {
            return new WidgetCache(...lots of params);
        }
    };

    /** Provide a single separate WidgetCache for each thread. */
    @Provides WidgetCache provideWidgetCache() {
        return threadWidgetCache.get();
    }
}

当然,如果你想这样做更多与一个对象相比,您必须为要缓存的每个键编写一个ThreadLocal,然后为每个键创建一个提供程序。这似乎有点过分,这就是自定义范围的来源。

Of course, if you want to do that for more than one object, you'd have to write a ThreadLocal for every single key you want to cache, and then create a provider for each. That seems a little excessive, and that's where custom scopes come in.

检查out Scope唯一有意义的方法:

Check out Scope's only meaningful method:

/**
 * Scopes a provider. The returned provider returns objects from this scope.
 * If an object does not exist in this scope, the provider can use the given
 * unscoped provider to retrieve one.
 *
 * <p>Scope implementations are strongly encouraged to override
 * {@link Object#toString} in the returned provider and include the backing
 * provider's {@code toString()} output.
 *
 * @param key binding key
 * @param unscoped locates an instance when one doesn't already exist in this
 *  scope.
 * @return a new provider which only delegates to the given unscoped provider
 *  when an instance of the requested object doesn't already exist in this
 *  scope
 */
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped);

正如您从范围界面,范围只是提供者,并确定线程本地的某些内容相当于返回 ThreadLocal -cached副本(如果存在)或缓存并从传递的<$返回c $ c>提供商如果没有。因此,我们可以轻松编写一个与我们手动执行相同逻辑的范围。

As you can see from the Scope interface, a scope is just a decorator for a Provider, and scoping something thread-local is tantamount to returning a ThreadLocal-cached copy if it exists or caching and returning from the passed Provider if it doesn't. So we can easily write a scope that does the same logic we did manually above.

实际上,需要为每个线程创建一个新的FooObject(对于任何值FooObject)是一个常见的请求 - 过多的高级功能对于基础库,但通常足以成为有关如何编写自定义范围的示例。要根据需要调整SimpleScope示例,可以省略 scope.enter() scope.exit()调用,但保持 ThreadLocal< Map< Key<?>,Object>> 充当对象的线程局部缓存。

In fact, the need to create a new FooObject per-thread (for any value of FooObject) is a common request—too much of an "advanced feature" for the base library, but common enough for it to be the example about how to write a custom scope. To adjust that SimpleScope example to your needs, you can leave out the scope.enter() and scope.exit() calls, but keep the ThreadLocal<Map<Key<?>, Object>> to act as your thread-local cache of objects.

此时,假设您使用 ThreadScope @ThreadScoped 注释c>您编写的实现,您可以将模块调整为如下所示:

At that point, assuming you've created your own @ThreadScoped annotation with a ThreadScope implementation you've written, you can adjust your module to look like this:

public class CacheModule extends AbstractModule {
    @Override
    protected void configure() {
        bindScope(ThreadScoped.class, new ThreadScope());
    }

    /** Provide a single separate WidgetCache for each thread. */
    @Provides @ThreadScoped WidgetCache provideWidgetCache() {
        return new WidgetCache(...lots of params);
    }
}

请记住,单身行为不依赖于哪些线程你创建了模块,而不是你要问的那个注入器。如果您创建了五个不相关的 Injector 实例,它们每个都有自己的单例。如果您只是尝试以多线程方式运行一个小算法,那么您可以为每个线程创建自己的注入器,但这样您就不会有机会制作跨越线程的单例对象。

Remember, singleton behavior doesn't depend on which threads you create the modules in, but rather which injector you're asking. If you created five unrelated Injector instances, they would each have their own singleton. If you're merely trying to run a small algorithm in a multi-threaded way, you could create your own injector per thread, but that way you'd lose the chance to make singleton objects that do span threads.

这篇关于Guice单身人士是否尊重线程限制?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆