In my web application, I have a background service. This service uses Generator class that contains an Engine class and an ExecutorService configured to use multiple threads and that accepts 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 a singleton instance because the instance can’t be shared across multiple threads (it relies on sequential processing). It’s perfectly fine to reuse the instance though, after a processing task has completed.
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 might be another way to inject ThreadLocal variables using Spring annotations. I’ve looked at scoping the bean to request scope, but I’m not sure how I should go about it given my design.
Any guidance on how to improve my design would be appreciated.
First of all abandon
ThreadLocal– there is something scary in that class. What you need is just object pooling. It’s not well known feature, but Spring supports this as well:Now when you inject
engine, you’ll actually receive proxy object (Enginewill need an interface) that will delegate all calls to free object in the pool. Pool size is configurable. Of course there is nothing preventing you from usingThreadLocalTargetSourcewhich usesThreadLocalinstead of Commons Pool. Both approaches guarantee exclusive, thread safe access toEngine.Finally you can use pooling manually (but the beauty of solution above is that it’s completely transparent) or switch to EJBs, which are pooled by definition.