Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making Spring beans behave like ThreadLocal instances for an ExecutorService

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.

like image 524
Jensen Ching Avatar asked Sep 27 '12 03:09

Jensen Ching


People also ask

Can ThreadLocal be static?

ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

Are Spring beans Threadsafe?

A piece of code is thread-safe if it only manipulates shared data structures in a manner that guarantees safe execution by multiple threads at the same time. So eventually thread safety depends on the code and the code only. And this is the reason why Spring beans are not thread safe per se.

How is ThreadLocal implemented in Java?

You create a ThreadLocal instance just like you create any other Java object - via the new operator. Here is an example that shows how to create a ThreadLocal variable: private ThreadLocal threadLocal = new ThreadLocal(); This only needs to be done once per thread.

When should I use ThreadLocal?

Java ThreadLocal is used to create thread local variables. We know that all threads of an Object share it's variables, so the variable is not thread safe. We can use synchronization for thread safety but if we want to avoid synchronization, we can use ThreadLocal variables.


2 Answers

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:

<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 inject engine, you'll actually receive proxy object (Engine will 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 using ThreadLocalTargetSource which uses ThreadLocal instead of Commons Pool. Both approaches guarantee exclusive, thread safe access to Engine.

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.

like image 198
Tomasz Nurkiewicz Avatar answered Nov 15 '22 19:11

Tomasz Nurkiewicz


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

In order to use it you need to register a custom scope:

<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 thread-scoped bean:

<bean id="myBean" class="com.foo.MyBean" scope="thread">
    ...
</bean>
like image 36
helmy Avatar answered Nov 15 '22 20:11

helmy