Creating Spring beans behaves like ThreadLocal instances for ExecutorService

In my web application, I have a background service. This service uses the Generator class, which contains the Engine and ExecutorService classes, configured to use multiple threads and accept GeneratorTasks.

 @Component public class Generator { @Autowired private Engine heavyEngine; private ExecutorService exec = Executors.newFixedThreadPool(3); //I actually pass the singleton instance Generator class into the task. public void submitTask(TaskModel model, TaskCallback callback) { this.exec.submit(new GeneratorTask(model, this, callback)); } } @Component public class Engine { public Engine() { //time-consuming initialization code here } } public class GeneratorTask implements Callable<String> { public GeneratorTask(TaskModel m, Generator g, ReceiptCallback c) { this.m = m; this.generator = g; this.c = c; } public String call() throws Exception { //This actually calls the Engine class of the generator. //Maybe I should have passed the Engine itself? this.generator.runEngine(c); } } 

The Engine class takes a long time to initialize, so I ideally want to initialize it only once per thread. I can't just make it an instance of singleton, because the instance cannot be used for multiple threads (it relies on sequential processing). It is great to reuse an instance, although after completing the processing task.

I was thinking of making the private Engine heavyEngine variable a ThreadLocal variable. However, I'm also new to Spring, so I was wondering if there could be another way to insert ThreadLocal variables using Spring annotations. I reviewed the scope of the bean before request , but I'm not sure how I can do this based on my design.

Any recommendations for improving my design would be appreciated.

+8
java spring multithreading
source share
3 answers

First of all, abandon ThreadLocal - there is something terrible in this class. What you need is just a union of objects. This is not a well-known feature, but Spring also supports this:

 <bean id="engineProto" class="Engine" scope="prototype" lazy-init="true"/> <bean id="engine" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="targetSource"> <bean class="org.springframework.aop.target.CommonsPoolTargetSource"> <property name="targetClass" value="Engine"/> <property name="targetBeanName" value="engineProto"/> <property name="maxSize" value="3"/> <property name="maxWait" value="5000"/> </bean> </property> </bean> 

Now, when you enter engine , you will actually receive a proxy object ( engine will require an interface), which delegates all calls to a free object in the pool. The size of the pool is customizable. Of course, there is nothing that would prevent you from using ThreadLocalTargetSource , which uses ThreadLocal instead of the Commons Pool . Both approaches guarantee exclusive, thread-safe access to the engine .

Finally, you can use the pool manually (but the beauty of the solution above is completely transparent) or switch to EJBs, which are combined by definition.

+9
source share

FYI, Spring 3.0 and later includes a thread- aware Scope implementation, SimpleThreadScope .

To use it, you need to register a custom area:

 <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="thread"> <bean class="org.springframework.context.support.SimpleThreadScope" /> </entry> </map> </property> </bean> 

And then to declare a bean:

 <bean id="myBean" class="com.foo.MyBean" scope="thread"> ... </bean> 
+5
source share

I would create a factory for Engine and name it inside GeneratorTask . This way you can remove the heavyEngine field inside the Generator and the argument of the Generator constructor in the GeneratorTask .
Then, if you want to preserve Engine initialization time, you can still declare it as singleton, but use the synchronized for non-thread methods.

 public class Generator { @Autowired private EngineFactory engineFactory; private ExecutorService exec = Executors.newFixedThreadPool(3); public void submitTask(TaskModel model, TaskCallback callback) { this.exec.submit(new GeneratorTask(engineFactory, model, callback)); } } public class EngineFactory { @Autowired private Engine instance; public Engine getInstance() { return instance; } } public class Engine { public Engine() { //time-consuming initialization code here } public synchronized void runEngine() { // Do non thread safe stuf } } public class GeneratorTask implements Callable<String> { public GeneratorTask(EngineFactory f, TaskModel m, ReceiptCallback c) { this.f = f; this.m = m; this.c = c; } public String call() throws Exception { Engine engine = f.getInstance(); engine.runEngine(); ... } } 

There is probably a clean way for Spring to pass the Callable engine, but in this case, the factory is pretty good, in my opinion.

+1
source share

All Articles