Initial Note 1
Instead of creating and starting threads by hand, I would suggest to use a pool of threads that is externally configured, so that you can manage the number of threads that are created. If the size of someList
is 1000, creating so many threads is inefficient. You should better use an executor backed by a pool of threads. Spring provides some implementations that can be used as spring beans configured with the task
namespace, something like this:
<task:executor id="executor" queue-capacity="10" rejection-policy="CALLER_RUNS" />
queue-capacity
is the max size of the threads pool. If that size is exceeded, the current thread will run the additional task, thus blocking the loop until another thread is freed (rejection-policy="CALLER_RUNS"
). See the task:executor
documentation, or define any ThreadPoolExecutor
(spring or jdk-concurrent) with your own configuration.
Initial Note 2
If the only state that you intend to store in MyClassImpl
is the item from the list, then you can forget the rest of the explanation below (except for the ThreadPool stuff), and directly use a singleton bean : remove the Runnable
interface and its no-arg run()
method, add a run(OtherClass obj)
method and do something like this:
final MyInterface task = // get it from spring as a singleton
for (final OtherClass obj : someList) {
executor.execute(new Runnable() {
public void run() {task.run(obj);}
});
// jdk 8 : executor.execute(task::run);
}
If you plan to store some state inside MyClassImpl during the execution of run()
(other than the processed object), go on reading. But you will still use the run(OtherClass obj)
method instead of no-args run()
.
The basic idea is to get a different object for each running thread, based on some kind of model or prototype defined as a spring bean. In order to achieve this, just define the bean that you initially want to pass to each thread as a proxy that dispatches to an instance that is bound to the running thread. This means that the same instance of task is injected into each thread, and during the thread execution, the real task on which you invoke methods is bound to the current thread.
Main program
Since you are using the elements of the list to do your business, you will pass each element to its owning task.
public class Program {
@Resource private MyInterface task; // this is a proxy
@Resource private TaskExecutor executor;
public void executeConcurrently(List<OtherClass> someList) {
for (final OtherClass obj : someList) {
executor.execute(new Runnable() {
public void run() { task.run(obj); }
});
// jdk 8 : executor.execute(task::run);
}
}
}
We suppose that Program
is a spring bean, thus the dependencies can be injected. If Program
is not a spring bean, you will need to get the spring ApplicationContext from somewhere, then autowire Program
(i.e. inject dependencies found in the ApplicationContext, based on annotations). Something like this (in the constructor) :
public Program(ApplicationContext ctx) {
ctx.getAutowireCapableBeanFactory().autowireBean(this);
}
Define the task
<bean id="taskTarget" class="MyImplClass" scope="prototype" autowire-candidate="false" />
<bean id="task" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetSource">
<bean class="org.springframework.aop.target.ThreadLocalTargetSource">
<property name="targetBeanName" value="taskTarget"/>
<property name="targetClass" value="MyInterface"/>
</bean>
</property>
</bean>
taskTarget
is where you define your business. This bean is defined as a prototype, as a new instance will be allocated to each thread. Thanks to this, you can even store state that depends on the run()
parameter. This bean is never used directly by the application (thus autowire-candidate="false"
), but it is used through the task
bean. In executeConcurrently()
above, the line task.run(obj)
will actually be dispatched on one of the prototype taskTarget
that was created by the proxy.