Not only will Guice provide the same singleton for threads for the same injector, but Guice can only provide the same singleton for threads if you use toInstance . Modules are evaluated once per injector, and you provided Guice with one instance and could not create a second.
Guice is not magic. When trying to provide an instance of an object, it requires (1) a constructor without arguments, no arguments, or @Inject -annotated; (2) a Provider or @Provides , allowing you to instantiate yourself; or (3) an instance that you have already created and associated with toInstance , which Guice repeats because it does not know how to create another. Note that option 2 with the Provider does not have to guarantee that it creates a new instance each time, and we can use this to write the Provider with the ThreadLocal cache. It will look something like this:
public class CacheModule extends AbstractModule { @Override protected void configure() {} private ThreadLocal<WidgetCache> threadWidgetCache = new ThreadLocal<>() { @Override protected WidgetCache initialValue() { return new WidgetCache(...lots of params); } }; @Provides WidgetCache provideWidgetCache() { return threadWidgetCache.get(); } }
Of course, if you want to do this for more than one object, you will have to write ThreadLocal for each individual key that you want to cache, and then create a provider for each. This seems a little excessive, and that is where special areas appear.
Creating Your Own ThreadLocal Area
Mark the scope of only the meaningful method:
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped);
As you can see from the Scope interface , the scope is just a decorator for the Provider , and the scope is a local thread equivalent to returning a copy of ThreadLocal -cached if it exists or is cached and returned from the passed Provider if it is not. Therefore, we can easily write a scope that does the same logic as us manually.
In fact, the need to create a new FooObject for the stream (for any FooObject value) is a common request for the "extended function" "too large for the base library , but a fairly common example for him on how to write a custom scope . To adapt this SimpleScope example to your needs , you can leave calls to scope.enter() and scope.exit() , but keep ThreadLocal<Map<Key<?>, Object>> as your local object cache.
At this point, assuming that you created your own @ThreadScoped annotation with the ThreadScope implementation you wrote, you can configure your module this way:
public class CacheModule extends AbstractModule { @Override protected void configure() { bindScope(ThreadScoped.class, new ThreadScope()); } @Provides @ThreadScoped WidgetCache provideWidgetCache() { return new WidgetCache(...lots of params); } }
Remember that single-user behavior does not depend on which threads you create in the modules, but rather which injector you ask. If you created five unrelated Injector instances, each of them will have its own singleton. If you are just trying to run a small algorithm in a multi-threaded way, you can create your own injector for each thread, but in this way you will lose the ability to create single objects that span the threads.